Home  >  Article  >  Web Front-end  >  In-depth understanding of how to use Symbol in JavaScript_Basic knowledge

In-depth understanding of how to use Symbol in JavaScript_Basic knowledge

WBOY
WBOYOriginal
2016-05-16 15:48:301447browse

What is Symbol?

Symbols are not icons, nor do they mean that small images can be used in code:

2015728114935470.jpg (291×49)

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.

  • In other code, the properties you create will be enumerated by for-in or Object.keys();
  • The same method may have been used in some other libraries (setting the same attributes on the elements), then this will conflict with your code and produce unpredictable results;
  • Some other libraries may use the same approach in the future, which will also conflict with your code;
  • The standards committee may add a .isMoving() native method to each element, and your code will not work at all.

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;

The

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("isMoving") 中的 "isMoving" 字符串被称为 Symbol 的描述信息,这对调试非常有帮助。可以通过 console.log(isMoving) 打印出来,或通过 isMoving.toString() 将 isMoving 转换为字符串时,或在一些错误信息中显示出来。
  •     element[isMoving] 访问的是 symbol-keyed 属性,除了属性名是 Symbol 类型的值之外,与其它属性都一样。
  •     和数组一样,symbol-keyed 属性不能通过 . 操作符来访问,必须使用方括号的方式。
  •     操作 symbol-keyed 属性也非常方便,通过上面代码我们已经知道如何获取和设置 element[isMoving] 的值,我们还可以这样使用:if (isMoving in element) 或 delete element[isMoving]。
  •     另一方面,只有在 isMoving 的作用域范围内才可以使用上述代码,这可以实现弱封装机制:在一个模块内创建一些 Symbol,只有在该模块内部的对象才能使用,而不用担心与其它模块的代码发生冲突。

由于 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

  1. Symbol() returns a unique Symbol each time it is called.
  2. Symbol.for(string) returns the corresponding Symbol from the Symbol registry. Unlike the previous method, the Symbols in the Symbol registry are shared. That is, if you call Symbol.for("cat") three times, the same Symbol will be returned. The registry is very useful when different pages or different modules of the same page need to share Symbols.
  3. Symbol.iterator returns some Symbols predefined by the language, each with its own special purpose.

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.)

  • Make instanceof extensible. In ES6, the object instanceof constructor expression is standardized to a method of the constructor: constructor[Symbol.hasInstance](object), which means it is extensible.
  • Eliminate conflicts between new features and old code.
  • Support new types of string matching. In ES5, when str.match(myObject) is called, it first attempts to convert myObject to a RegExp object. In ES6, myObject will first be checked for a myObject[Symbol.match](str) method, and a custom string parsing method can be provided wherever regular expressions work.

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.

Statement:
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn