Maison  >  Article  >  interface Web  >  Compréhension approfondie de la façon d'utiliser Symbol dans JavaScript_Basic knowledge

Compréhension approfondie de la façon d'utiliser Symbol dans JavaScript_Basic knowledge

WBOY
WBOYoriginal
2016-05-16 15:48:301497parcourir

Qu'est-ce que le symbole ?

Les symboles ne sont pas des icônes et ne signifient pas non plus que de petites images peuvent être utilisées dans le code :

2015728114935470.jpg (291×49)

n'est pas non plus une syntaxe pour faire référence à autre chose. Alors, qu’est-ce que Symbole exactement ?
Sept types de données

Lorsque JavaScript a été standardisé en 1997, il existait 6 types de données. Jusqu'à l'avènement d'ES6, les variables du programme devaient être l'un des 6 types de données suivants :

Indéfini
Nul
Booléen
Numéro
Chaîne
Objet

Chaque type de données est une combinaison d'une série de valeurs, et le nombre de valeurs des cinq premiers types de données est limité. Le type booléen n'a que deux valeurs : vrai et faux Lors de l'affectation d'une valeur à une variable de type booléen, aucune nouvelle valeur n'est générée (les deux valeurs vrai et faux sont partagées). Pour Number et String, leurs valeurs sont bien plus. L'instruction standard est qu'il existe 18 437 736 874 454 810 627 valeurs de type Number (y compris NAN). Le nombre de types String est difficile à compter. Au départ, je pensais que c'était (2144,115,188,075,855,872 ? 1) ÷ 65,535... mais j'avais peut-être tort.

Le nombre de valeurs d'objet est illimité et chaque objet est unique. Chaque fois qu'une page Web est ouverte, une série d'objets est créée.

Le symbole dans ES6 est aussi un type de données, mais ce n'est pas une chaîne ou un objet, mais un nouveau type de données : le septième type de données.

Regardons un scénario dans lequel Symbol peut s'avérer utile.
Une question soulevée par une valeur booléenne

Parfois, il est très pratique de stocker temporairement certaines données appartenant à d'autres objets dans un autre objet. Par exemple, supposons que vous écriviez une bibliothèque JS qui utilise des transitions en CSS pour faire voler un élément DOM sur l'écran. Vous savez déjà que vous ne pouvez pas appliquer plusieurs transitions au même div en même temps, sinon l'animation sera très disgracieuse. . Vous avez un moyen de contourner ce problème, mais vous devez d'abord savoir si le div bouge déjà.

Comment résoudre ce problème ?

Une solution consiste à utiliser l'API fournie par le navigateur pour détecter si l'élément est dans un état animé, mais c'est une perte de temps. Lorsque vous configurez l'élément pour qu'il se déplace, votre bibliothèque sait que l'élément se déplace.

Ce dont vous avez vraiment besoin, c'est d'un mécanisme pour garder une trace des éléments en mouvement, vous pouvez enregistrer les éléments en mouvement dans un tableau, et chaque fois que vous souhaitez animer un élément, vérifiez d'abord si l'élément est déjà dans celui-ci. dans la liste.

Ahem, mais si votre tableau est très grand, même une recherche linéaire comme celle-ci peut créer des problèmes de performances.

Donc, ce que vous voulez vraiment faire, c'est définir un indicateur directement sur l'élément :

if (element.isMoving) {
 smoothAnimations(element);
}
element.isMoving = true;

 
if (element.isMoving) {
 smoothAnimations(element);
}
element.isMoving = true;

Cela présente également des problèmes potentiels, et vous devez reconnaître le fait qu'il existe un autre code qui peut également fonctionner sur cet élément ODM.

  • Dans un autre code, les propriétés que vous créez seront énumérées par for-in ou Object.keys();
  • La même méthode peut avoir été utilisée dans d'autres bibliothèques (en définissant les mêmes attributs sur les éléments), cela entrera alors en conflit avec votre code et produira des résultats imprévisibles
  • ;
  • Certaines autres bibliothèques pourraient utiliser la même approche à l'avenir, ce qui entrera également en conflit avec votre code
  •  ;
  • Le comité des standards peut ajouter une méthode native .isMoving() à chaque élément, et votre code ne fonctionnera pas du tout.
Bien sûr, pour les trois dernières questions, vous pouvez choisir une chaîne dénuée de sens que personne n'utilisera :

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;

Cela semble tellement peu fiable que ça me fait mal aux yeux de le voir.

Vous pouvez également utiliser des algorithmes de chiffrement pour générer une chaîne presque unique :

// 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;

La syntaxe
object[name] nous permet d'utiliser n'importe quelle chaîne comme nom de propriété, le code fonctionnera normalement, les conflits sont presque impossibles et le code sera bien meilleur.

Cependant, cela conduit à une mauvaise expérience de débogage. Chaque fois que vous utilisez console.log() pour imprimer l'élément contenant cet attribut, vous verrez une énorme chaîne inutile, et que se passe-t-il s'il existe plusieurs de ces attributs ? ? Les noms des attributs ont changé après chaque actualisation. Comment rendre ces attributs plus intuitifs ?

Pourquoi est-ce si difficile ? Nous économisons juste un petit peu de drapeau.


Utilisez Symbol pour résoudre des problèmes

Les valeurs de symboles peuvent être créées par programme et utilisées comme noms d'attribut sans se soucier des conflits de noms d'attribut.

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

Une telle coercition doit être évitée et String(sym) ou sym.toString() doit être utilisé pour la conversion.
Trois façons d'obtenir le symbole

  1. Symbol() renvoie un symbole unique à chaque fois qu'il est appelé.
  2. Symbol.for(string) renvoie le symbole correspondant du registre de symboles Contrairement à la méthode précédente, les symboles du registre de symboles sont partagés. Autrement dit, si vous appelez Symbol.for("cat") trois fois, le même Symbol sera renvoyé. Le registre est très utile lorsque différentes pages ou différents modules d'une même page doivent partager des symboles.
  3. Symbol.iterator renvoie des symboles prédéfinis par le langage, chacun avec son propre objectif.

Si vous n'êtes toujours pas sûr de l'utilité des symboles, la section suivante sera très intéressante car je vais vous montrer les symboles en action.
Application du symbole dans la spécification ES6

Nous savons déjà que nous pouvons utiliser Symbol pour éviter les conflits de code. Lors de l'introduction de l'itérateur auparavant, nous avons également analysé que for (var item of myArray) commence en interne par l'appel de myArray[Symbol.iterator](). À cette époque, j'ai mentionné que cette méthode pouvait être remplacée par myArray.iterator(), mais en utilisant. Le symbole a une meilleure compatibilité ascendante.

Il existe encore des endroits où Symbol est utilisé dans ES6. (Ces fonctionnalités ne sont pas encore implémentées dans FireFox.)

  • Rendre instanceof extensible. Dans ES6, l'expression du constructeur d'instance d'objet est standardisée en une méthode du constructeur : constructor[Symbol.hasInstance](object), ce qui signifie qu'elle est extensible.
  • Éliminez les conflits entre les nouvelles fonctionnalités et l'ancien code.
  • Prise en charge de nouveaux types de correspondance de chaînes. Dans ES5, lorsque str.match(myObject) est appelé, il tente d'abord de convertir myObject en objet RegExp. Dans ES6, myObject sera d'abord vérifié pour une méthode myObject[Symbol.match](str), et une méthode d'analyse de chaîne personnalisée peut être fournie partout où les expressions régulières fonctionnent.

Ces usages sont encore relativement restreints, mais il est difficile de voir l'impact significatif de ces nouvelles fonctionnalités rien qu'en regardant le code de mon article. Le symbole JavaScript est une version améliorée de __doubleUnderscores en PHP et Python, et les organismes de normalisation l'utiliseront pour ajouter de nouvelles fonctionnalités au langage sans affecter le code existant.

Déclaration:
Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn