Par spĂ©cification, seuls deux types primitifs peuvent servir de clĂ©s de propriĂ©tĂ© dâobjet :
- type string, ou
- type symbol.
Sinon, si lâon utilise un autre type, tel que nombre, il est automatiquement converti en chaĂźne de caractĂšres. Ainsi, obj[1] est identique Ă obj["1"], et obj[true] est identique Ă obj["true"].
JusquâĂ prĂ©sent, nous nâutilisions que des chaĂźnes de caractĂšres.
Explorons maintenant les symboles, voyons ce quâils peuvent faire pour nous.
Symboles
Un âSymbolâ reprĂ©sente un identifiant unique.
Une valeur de ce type peut ĂȘtre créée en utilisant Symbol() :
let id = Symbol();
Lors de la création, nous pouvons donner au symbole une description (également appelée nom de symbole), particuliÚrement utile pour le débogage :
// id est un symbole avec la description "id"
let id = Symbol("id");
Les symboles sont garantis dâĂȘtre uniques. MĂȘme si nous crĂ©ons beaucoup de symboles avec la mĂȘme description, ce sont des valeurs diffĂ©rentes. La description est juste une Ă©tiquette qui nâaffecte rien.
Par exemple, voici deux symboles avec la mĂȘme description â ils ne sont pas Ă©gaux :
let id1 = Symbol("id");
let id2 = Symbol("id");
alert(id1 == id2); // false
Si vous connaissez Ruby ou un autre langage qui comporte Ă©galement une sorte de âsymbolesâ, attention Ă ne pas vous tromper. Les symboles JavaScript sont diffĂ©rents.
Donc, pour rĂ©sumer, un symbole est une âvaleur unique primitiveâ avec une description facultative. Voyons oĂč nous pouvons les utiliser.
La plupart des valeurs de JavaScript prennent en charge la conversion implicite en chaßne de caractÚres. Par exemple, nous pouvons alert presque toutes les valeurs et cela fonctionnera. Les symboles sont spéciaux. Ils ne se convertissent pas automatiquement.
Par exemple, cette alert affichera une erreur :
let id = Symbol("id");
alert(id); // TypeError: Impossible de convertir une valeur de symbole en chaĂźne de caractĂšres
Câest un âgardien du langageâ contre les erreurs, parce que les chaĂźnes de caractĂšres et les symboles sont fondamentalement diffĂ©rents et ne doivent accidentellement pas ĂȘtre convertis les uns en les autres.
Si nous voulons vraiment afficher un symbole, nous devons appeler .toString() dessus, comme ici :
let id = Symbol("id");
alert(id.toString()); // Symbol(id), maintenant ça marche
Ou récupérer la propriété symbol.description pour afficher la description uniquement :
let id = Symbol("id");
alert(id.description); // id
PropriĂ©tĂ©s âcachĂ©esâ
Les symboles nous permettent de crĂ©er des propriĂ©tĂ©s âcachĂ©esâ dâun objet, quâaucune autre partie du code ne peut accĂ©der accidentellement ou Ă©craser.
Par exemple, si nous travaillons avec des objets user qui appartiennent Ă un code tiers, nous aimerions leur ajouter des identificateurs.
Utilisons une clé symbole pour cela :
let user = { // belongs to another code
name: "John"
};
let id = Symbol("id");
user[id] = 1;
alert( user[id] ); // nous pouvons accéder aux données en utilisant le symbole comme clé
Quel est lâavantage de lâutilisation de Symbol("id") sur une chaĂźne de caractĂšres "id" ?
Poussons un peu plus loin lâexemple pour voir cela.
Comme les objets user appartiennent Ă une autre base de code, il nâest pas sĂ»r de leur ajouter des champs, car nous pourrions affecter le comportement prĂ©dĂ©fini dans cette autre base de code. Cependant, les symboles ne peuvent pas ĂȘtre accĂ©dĂ©s accidentellement. Le code tiers ne sera pas conscient des symboles nouvellement dĂ©finis, il est donc prudent dâajouter des symboles aux objets user.
Imaginez quâun autre script veuille avoir son propre identifiant Ă lâintĂ©rieur de user, pour sa propre utilisation.
Ensuite, ce script peut créer son propre symbol("id"), comme ceci :
// ...
let id = Symbol("id");
user[id] = "Their id value";
Il nây aura pas de conflit entre nos identificateurs et les leurs, car les symboles sont toujours diffĂ©rents, mĂȘme sâils portent le mĂȘme nom.
Notez que si nous utilisions une chaĂźne de caractĂšre "id" au lieu dâun symbole dans le mĂȘme but, il y aurait un conflit :
let user = { name: "John" };
// Notre script utilise la propriété "id"
user.id = "Our id value";
// ...Un autre script veut aussi "id" pour ses besoins âŠ
user.id = "Their id value"
// Boom! écrasé par un autre script!
Symboles dans un objet littéral
Si nous voulons utiliser un symbole dans un objet littéral {...}, nous avons besoin de crochets.
Comme ceci :
let id = Symbol("id");
let user = {
name: "John",
[id]: 123 // pas "id": 123
};
Câest parce que nous avons besoin de la valeur de la variable id comme clĂ©, pas de la chaĂźne de caractĂšres âidâ.
Les symboles sont ignorĂ©s par forâŠin
Les propriétés symboliques ne participent pas à la boucle for..in.
Par exemple :
let id = Symbol("id");
let user = {
name: "John",
age: 30,
[id]: 123
};
for (let key in user) alert(key); // name, age (pas de symboles)
// l'accĂšs direct par le symbole fonctionne
alert( "Direct: " + user[id] ); // Direct: 123
Object.keys(user) les ignore Ă©galement. Cela fait partie du principe gĂ©nĂ©ral du âdissimulation des propriĂ©tĂ©s symboliquesâ. Si un autre script ou une bibliothĂšque parcourt notre objet, il nâaccĂ©dera pas de maniĂšre inattendue Ă une propriĂ©tĂ© symbolique.
En revanche, Object.assign copie les propriétés de chaßne de caractÚres et de symbole :
let id = Symbol("id");
let user = {
[id]: 123
};
let clone = Object.assign({}, user);
alert( clone[id] ); // 123
Il nây a pas de paradoxe ici. Câest par conception. LâidĂ©e est que lorsque nous clonons un objet ou que nous fusionnons des objets, nous souhaitons gĂ©nĂ©ralement que toutes les propriĂ©tĂ©s soient copiĂ©es (y compris les symboles tels que id).
Symboles globaux
Comme nous lâavons vu, habituellement tous les symboles sont diffĂ©rents, mĂȘme sâils portent les mĂȘmes noms. Mais parfois, nous voulons que les symboles portant le mĂȘme nom soient les mĂȘmes entitĂ©s. Par exemple, diffĂ©rentes parties de notre application veulent accĂ©der au symbole "id" qui signifie exactement la mĂȘme propriĂ©tĂ©.
Pour cela, il existe un registre de symboles global. Nous pouvons crĂ©er des symboles et y accĂ©der ultĂ©rieurement, ce qui garantit que les accĂšs rĂ©pĂ©tĂ©s portant le mĂȘme nom renvoient exactement le mĂȘme symbole.
Pour lire (crĂ©er en cas dâabsence) un symbole du registre, utilisez Symbol.for(key).
Cet appel vĂ©rifie le registre global et, sâil existe un symbole dĂ©crit comme key, le renvoie, sinon il crĂ©e un nouveau symbole Symbol(key) et le stocke dans le registre avec la key donnĂ©e.
Par exemple :
// lit le registre global
let id = Symbol.for("id"); // si le symbole n'existait pas, il est créé
// relit le registre (peut-ĂȘtre Ă partir d'une autre partie du code)
let idAgain = Symbol.for("id");
// le mĂȘme symbole
alert( id === idAgain ); // true
Les symboles Ă lâintĂ©rieur de ce registre sont appelĂ©s symboles globaux. Si nous voulons un symbole Ă lâĂ©chelle de lâapplication, accessible partout dans le code, câest ce moyen que nous allons utiliser.
Dans certains langages de programmation, comme Ruby, il existe un seul symbole par nom.
Comme nous pouvons le constater, en JavaScript, câest vrai pour les symboles globaux.
Symbol.keyFor
Nous avons vu que pour les symboles globaux, Symbol.for(key) renvoie un symbole par son nom. Pour faire le contraire â retourner un nom par symbole global â nous pouvons utiliser : Symbol.keyFor(sym)Â :
Par exemple :
// get symbol by name
let sym = Symbol.for("name");
let sym2 = Symbol.for("id");
// obtenir le nom par symbole
alert( Symbol.keyFor(sym) ); // name
alert( Symbol.keyFor(sym2) ); // id
Symbol.keyFor utilise en interne le registre de symboles global pour rechercher la clĂ© du symbole. Donc, cela ne fonctionne pas pour les symboles non globaux. Si le symbole nâest pas global, il ne pourra pas le trouver et retournera undefined.
Cela dit, tous les symboles ont la propriété description.
Par exemple :
let globalSymbol = Symbol.for("name");
let localSymbol = Symbol("name");
alert( Symbol.keyFor(globalSymbol) ); // name, global symbol
alert( Symbol.keyFor(localSymbol) ); // undefined, not global
alert( localSymbol.description ); // name
System symbols
Il existe de nombreux âsystĂšmesâ symboles que JavaScript utilise en interne et que nous pouvons utiliser pour affiner divers aspects de nos objets.
Ils sont listés dans la documentation Well-known symbols :
Symbol.hasInstanceSymbol.isConcatSpreadableSymbol.iteratorSymbol.toPrimitive- Etc.
Par exemple, Symbol.toPrimitive nous permet de dĂ©crire une conversion dâobjet en primitive. Nous verrons son utilisation trĂšs bientĂŽt.
Nous nous familiariserons Ă©galement avec dâautres symboles lorsque nous Ă©tudierons les caractĂ©ristiques du langage correspondantes.
Résumé
Symbol est un type primitif pour les identificateurs uniques.
Les symboles sont créés avec lâappel Symbol() ainsi quâune description facultative.
Les symboles sont toujours de valeurs diffĂ©rentes, mĂȘme sâils portent le mĂȘme nom. Si nous voulons que les symboles portant le mĂȘme nom soient Ă©gaux, nous devons utiliser le registre global : Symbol.for(key) renvoie (crĂ©e si nĂ©cessaire) un symbole global avec key comme nom.
Les multiples appels de Symbol.for avec la mĂȘme key renvoient exactement le mĂȘme symbole.
Les symboles ont deux principaux cas dâutilisation :
-
PropriĂ©tĂ©s dâobjet âmasquĂ©esâ. Si nous voulons ajouter une propriĂ©tĂ© Ă un objet qui âappartientâ Ă un autre script ou Ă une librairie, nous pouvons crĂ©er un symbole et lâutiliser comme clĂ© de propriĂ©tĂ©. Une propriĂ©tĂ© symbolique nâapparait pas dans
for..in, elle ne sera donc pas traitĂ©e accidentellement avec dâautres propriĂ©tĂ©s. De plus, elle ne sera pas accessible directement, car un autre script nâa pas notre symbole. Ainsi, la propriĂ©tĂ© sera protĂ©gĂ©e contre une utilisation accidentelle ou un Ă©crasement.Ainsi, nous pouvons âdissimulerâ quelque chose dans des objets dont nous avons besoin, mais que les autres ne devraient pas voir, en utilisant des propriĂ©tĂ©s symboliques.
-
De nombreux symboles systÚme utilisés par JavaScript sont accessibles en tant que
Symbol.*. Nous pouvons les utiliser pour modifier certains comportements internes. Par exemple, plus tard dans le tutoriel, nous utiliseronsSymbol.iteratorpour iterables,Symbol.toPrimitive, etc.
Techniquement, les symboles ne sont pas cachĂ©s Ă 100%. Il y a une mĂ©thode intĂ©grĂ©e Object.getOwnPropertySymbols(obj) qui nous permet dâobtenir tous les symboles. Il y a aussi une mĂ©thode nommĂ©e Reflect.ownKeys(obj) qui renvoie toutes les clĂ©s dâun objet, y compris celles symboliques. Donc, ils ne sont pas vraiment cachĂ©s. Mais la plupart des bibliothĂšques, fonctions intĂ©grĂ©es et structures de syntaxe nâutilisent pas ces mĂ©thodes.
Commentaires
<code>, pour plusieurs lignes â enveloppez-les avec la balise<pre>, pour plus de 10 lignes - utilisez une sandbox (plnkr, jsbin, codepenâŠ)