What is Symbol?
Symbols are not icons, nor do they mean that small images can be used in code:
is also not a syntax for referring to something else. So, what exactly is Symbol?
Seven data types
When JavaScript was standardized in 1997, there were 6 data types. Until the advent of ES6, variables in the program must be one of the following 6 data types:
Undefined
Null
Boolean
Number
String
Object
Each data type is a combination of a series of values, and the number of values of the first five data types is limited. The Boolean type has only two values: true and false. When assigning a value to a variable of the Boolean type, no new value is generated (the two values true and false are shared). For Number and String, their values are much more. The standard statement is that there are 18,437,736,874,454,810,627 Number type values (including NAN). The number of String types is difficult to count. I originally thought it was (2144,115,188,075,855,872 ? 1) ÷ 65,535...but maybe I was wrong.
The number of object values is unlimited, and each object is unique. Every time a web page is opened, a series of objects are created.
Symbol in ES6 is also a data type, but it is not a string or an object, but a new data type: the seventh data type.
Let’s look at a scenario where Symbol may come in handy.
A question raised by a Boolean value
Sometimes it is very convenient to temporarily store some data belonging to other objects in another object. For example, suppose you are writing a JS library that uses transitions in CSS to make a DOM element fly across the screen. You already know that you cannot apply multiple transitions to the same div at the same time, otherwise the animation will be very unsightly. You do have a way around this, but first you need to know if the div is already moving.
How to solve this problem?
One way is to use the API provided by the browser to detect whether the element is in an animated state, but it is a waste of time. When you set the element to move, your library knows that the element is moving.
What you really need is a mechanism to keep track of which elements are moving, you could save the moving elements in an array, and every time you want to animate an element, first check to see if the element is already in this in the list.
Ahem, but if your array is very large, even a linear search like this can create performance issues.
So, what you really want to do is set a flag directly on the element:
if (element.isMoving) { smoothAnimations(element); } element.isMoving = true; if (element.isMoving) { smoothAnimations(element); } element.isMoving = true;
This also has some potential problems, and you have to acknowledge the fact that there is other code that may also operate on this ODM element.
Of course, for the last three questions, you can choose a meaningless string that no one will use:
if (element.__$jorendorff_animation_library$PLEASE_DO_NOT_USE_THIS_PROPERTY$isMoving__) { smoothAnimations(element); } element.__$jorendorff_animation_library$PLEASE_DO_NOT_USE_THIS_PROPERTY$isMoving__ = true; if (element.__$jorendorff_animation_library$PLEASE_DO_NOT_USE_THIS_PROPERTY$isMoving__) { smoothAnimations(element); } element.__$jorendorff_animation_library$PLEASE_DO_NOT_USE_THIS_PROPERTY$isMoving__ = true;
This seems so unreliable that it hurts my eyes to see it.
You can also use encryption algorithms to generate an almost unique string:
// get 1024 Unicode characters of gibberish var isMoving = SecureRandom.generateName(); ... if (element[isMoving]) { smoothAnimations(element); } element[isMoving] = true; // get 1024 Unicode characters of gibberish var isMoving = SecureRandom.generateName(); ... if (element[isMoving]) { smoothAnimations(element); } element[isMoving] = true;
object[name] syntax allows us to use any string as a property name, the code will work normally, conflicts are almost impossible, and the code will look much better.
However, this leads to a bad debugging experience. Every time you use console.log() to print out the element containing this attribute, you will see a huge garbage string, and what if there is more than one such attribute? ? The attribute names have changed after each refresh. How to make these attributes look more intuitive?
Why is it so difficult? We're just saving a little flag bit.
Use Symbol to solve problems
Symbol values can be created programmatically and used as attribute names without worrying about attribute name conflicts.
var mySymbol = Symbol(); var mySymbol = Symbol();
调用 Symbol() 方法将创建一个新的 Symbol 类型的值,并且该值不与其它任何值相等。
与数字和字符串一样,Symbol 类型的值也可以作为对象的属性名,正是由于它不与任何其它值相等,对应的属性也不会发生冲突:
obj[mySymbol] = "ok!"; // guaranteed not to collide console.log(obj[mySymbol]); // ok! obj[mySymbol] = "ok!"; // guaranteed not to collide console.log(obj[mySymbol]); // ok!
下面是使用 Symbol 来解决上面的问题:
// create a unique symbol var isMoving = Symbol("isMoving"); ... if (element[isMoving]) { smoothAnimations(element); } element[isMoving] = true; // create a unique symbol var isMoving = Symbol("isMoving"); ... if (element[isMoving]) { smoothAnimations(element); } element[isMoving] = true;
上面代码需要注意几点:
由于 Symbol 的设计初衷是为了避免冲突,当遍历 JavaScript 对象时,并不会枚举到以 Symbol 作为建的属性,比如,for-in 循环只会遍历到以字符串作为键的属性,Object.keys(obj)和 Object.getOwnPropertyNames(obj) 也一样,但这并不意味着 Symbol 为键的属性是不可枚举的:使用 Object.getOwnPropertySymbols(obj) 这个新方法可以枚举出来,还有 Reflect.ownKeys(obj) 这个新方法可以返回对象中所有字符串和 Symbol 键。(我将在后面的文章中详细介绍 Reflect 这个新特性。)
库和框架的设计者将会发现很多 Symbol 的用途,稍后我们将看到,JavaScript 语言本身也对其有广泛的应用。
Symbol 究竟是什么呢
> typeof Symbol() "symbol" > typeof Symbol() "symbol"
Symbol 是完全不一样的东西。一旦创建后就不可更改,不能对它们设置属性(如果在严格模式下尝试这样做,你将得到一个 TypeError)。它们可以作为属性名,这时它们和字符串的属性名没有什么区别。
另一方面,每个 Symbol 都是独一无二的,不与其它 Symbol 重复(即便是使用相同的 Symbol 描述创建),创建一个 Symbol 就跟创建一个对象一样方便。
ES6 中的 Symbol 与传统语言(如 Lisp 和 Ruby)中的 Symbol 中的类似,但并不是完全照搬到 JavaScript 中。在 Lisp 中,所有标识符都是 Symbol;在 JavaScript 中,标识符和大多数属性仍然是字符串,Symbol 只是提供了一个额外的选择。
值得注意的是:与其它类型不同的是,Symbol 不能自动被转换为字符串,当尝试将一个 Symbol 强制转换为字符串时,将返回一个 TypeError。
> var sym = Symbol("<3"); > "your symbol is " + sym // TypeError: can't convert symbol to string > `your symbol is ${sym}` // TypeError: can't convert symbol to string > var sym = Symbol("<3"); > "your symbol is " + sym // TypeError: can't convert symbol to string > `your symbol is ${sym}` // TypeError: can't convert symbol to string
Such coercion should be avoided and String(sym) or sym.toString() should be used for conversion.
Three ways to get Symbol
If you're still not sure if Symbols are useful, the next section will be very interesting as I'll show you Symbols in action.
Application of Symbol in ES6 specification
We already know that we can use Symbol to avoid code conflicts. When introducing iterator before, we also analyzed that for (var item of myArray) internally starts with calling myArray[Symbol.iterator](). At that time, I mentioned that this method can be replaced by myArray.iterator(), but using Symbol has better backward compatibility.
There are still some places where Symbol is used in ES6. (These features are not yet implemented in FireFox.)
These uses are still relatively narrow, but it is difficult to see the significant impact of these new features just by looking at the code in my article. JavaScript's Symbol is an improved version of __doubleUnderscores in PHP and Python, and standards organizations will use it to add new features to the language without affecting existing code.