Quelle est la motivation pour apporter des symboles à ES6?

368

MISE À JOUR : Récemment, un brillant article de Mozilla a été publié. Lisez-le si vous êtes curieux.

Comme vous le savez peut-être, ils prévoient d'inclure un nouveau type primitif de symbole dans ECMAScript 6 (sans parler d'autres choses folles). J'ai toujours pensé que la :symbolnotion de Ruby était inutile; nous pourrions facilement utiliser des chaînes simples à la place, comme nous le faisons en JavaScript. Et maintenant, ils décident de compliquer les choses avec JS.

Je ne comprends pas la motivation. Quelqu'un pourrait-il m'expliquer si nous avons vraiment besoin de symboles en JavaScript?

Yanis
la source
6
Je ne sais pas à quel point cette explication est authentique, mais c'est un début: tc39wiki.calculist.org/es6/symbols .
Felix Kling
8
Les symboles permettent tellement , ils permettent des identificateurs uniques étendus sur les objets. Par exemple, avoir des propriétés sur des objets qui ne sont accessibles qu'en un seul endroit.
Benjamin Gruenbaum
5
Je n'en suis pas sûr car vous pouvez utiliser Object.getOwnPropertySymbols (o)
Yanis
4
C'est plus d'unicité que d'intimité.
Qantas 94 Heavy
2
Ils allaient avoir une implémentation de classe plus compliquée avec privateet publicdes mots-clés d'attribut de classe qu'ils ont décidé d'abandonner pour une implémentation de classe plus simple. Au lieu de this.x = xvous, vous étiez censé le faire public x = xet pour des variables privées private y = y. Ils ont décidé d'abandonner cela pour une implémentation de classe beaucoup plus minimale. Symbol serait alors une solution de contournement requise pour obtenir des propriétés privées dans l'implémentation minimale.
lyschoening

Réponses:

224

La motivation initiale pour introduire des symboles dans Javascript était d'activer les propriétés privées .

Malheureusement, ils ont fini par être fortement dégradés. Ils ne sont plus privés, car vous pouvez les trouver via la réflexion, par exemple, à l'aide de Object.getOwnPropertySymbolsou des procurations.

Ils sont désormais connus sous le nom de symboles uniques et leur seule utilisation prévue est d'éviter les conflits de noms entre les propriétés. Par exemple, ECMAScript lui-même peut désormais introduire des hooks d'extension via certaines méthodes que vous pouvez mettre sur des objets (par exemple pour définir leur protocole d'itération) sans risquer de les heurter avec les noms d'utilisateurs.

Que ce soit une motivation assez forte pour ajouter des symboles à la langue est discutable.

Andreas Rossberg
la source
93
La plupart des langues (toutes les langues dominantes afaik) fournissent un mécanisme, généralement la réflexion, pour avoir accès au privé de toute façon.
Esailija
19
@Esailija, je ne pense pas que ce soit vrai - en particulier, car de nombreuses langues n'offrent pas de réflexion en premier lieu. Une fuite de l'état privé par la réflexion (comme par exemple en Java) doit être considérée comme un bug, pas comme une fonctionnalité. Cela est particulièrement vrai sur les pages Web, où un état privé fiable peut être important pour la sécurité. Actuellement, le seul moyen d'y parvenir dans JS est de fermer, ce qui peut être à la fois fastidieux et coûteux.
Andreas Rossberg
38
Le mécanisme ne doit pas nécessairement être une réflexion - C ++, Java, C #, Ruby, Python, PHP, Objective-C autorisent tous l'accès d'une manière ou d'une autre si l'on veut vraiment. Ce n'est pas vraiment une question de capacité mais de communication.
Esailija
4
@plalx, ​​sur le Web, l'encapsulation concerne parfois aussi la sécurité.
Andreas Rossberg
3
@RolandPihlakas, malheureusement, Object.getOwnPropertySymbolsn'est pas la seule fuite; la plus difficile est la possibilité d'utiliser des procurations pour intercepter l'accès à une propriété "privée".
Andreas Rossberg
95

Les symboles ne garantissent pas une véritable confidentialité mais peuvent être utilisés pour séparer les propriétés publiques et internes des objets. Prenons un exemple où nous pouvons utiliser Symbolpour avoir des propriétés privées.

Prenons un exemple où une propriété d'un objet n'est pas privée.

var Pet = (function() {
  function Pet(type) {
    this.type = type;
  }
  Pet.prototype.getType = function() {
    return this.type;
  }
  return Pet;
}());

var a = new Pet('dog');
console.log(a.getType());//Output: dog
a.type = null;
//Modified outside
console.log(a.getType());//Output: null

Ci-dessus, la Petpropriété de classe typen'est pas privée. Pour le rendre privé, nous devons créer une fermeture. L'exemple ci-dessous illustre comment nous pouvons rendre typeprivé en utilisant une fermeture.

var Pet = (function() {
  function Pet(type) {
    this.getType = function(){
      return type;
    };
  }
  return Pet;
}());

var b = new Pet('dog');
console.log(b.getType());//dog
b.type = null;
//Stays private
console.log(b.getType());//dog

Inconvénient de l'approche ci-dessus: nous introduisons une fermeture supplémentaire pour chaque Petinstance créée, ce qui peut nuire aux performances.

Maintenant, nous vous présentons Symbol. Cela peut nous aider à rendre une propriété privée sans utiliser de fermetures inutiles supplémentaires. Exemple de code ci-dessous:

var Pet = (function() {
  var typeSymbol = Symbol('type');
  function Pet(type) {
    this[typeSymbol] = type;
  }
  Pet.prototype.getType = function(){
    return this[typeSymbol];
  }
  return Pet;
}());

var a = new Pet('dog');
console.log(a.getType());//Output: dog
a.type = null;
//Stays private
console.log(a.getType());//Output: dog
Samar Panda
la source
15
Notez que les propriétés des symboles ne sont pas privées ! Les symboles sont sans collision . Vous voudrez peut-être lire la réponse acceptée.
Bergi
3
Oui, le symbole ne garantit pas une véritable confidentialité mais peut être utilisé pour séparer les propriétés publiques et internes des objets. Désolé, j'ai oublié d'ajouter ce point à ma réponse. Mettra à jour ma réponse en conséquence.
Samar Panda le
@SamarPanda, Vous pourriez tout aussi bien dire que le préfixage des membres _ne garantit pas une véritable confidentialité mais peut être utilisé pour séparer les propriétés publiques et internes des objets. En d'autres termes, réponse inutile.
Pacerier
10
Je ne dirais pas inutile, comme les symboles ne sont pas énumérables par défaut, il n'est pas non plus accessible par «erreur», alors que n'importe quelle autre clé le peut.
Patrick
5
Je trouve que votre réponse est la seule à avoir un exemple qui a du sens, pourquoi vous voudriez définir l'attribut privé de l'objet en tant que symbole, au lieu d'un simple attribut normal.
Luis Lobo Borobia
42

Symbolssont un nouveau type d'objet spécial qui peut être utilisé comme nom de propriété unique dans les objets. Utiliser Symbolau lieu de stringpermet à différents modules de créer des propriétés qui n'entrent pas en conflit les unes avec les autres. Symbolspeuvent également être rendus privés, afin que leurs propriétés ne soient pas accessibles à toute personne qui n'a pas déjà un accès direct à laSymbol .

Symbolssont une nouvelle primitive . Tout comme les number, stringet booleanprimitives, Symbolune fonction qui peut être utilisée pour les créer. Contrairement aux autres primitives, elles Symbolsn'ont pas de syntaxe littérale (par exemple, comment stringont '') - la seule façon de les créer est avec le Symbolconstructeur de la manière suivante:

let symbol = Symbol();

En réalité, Symbolles sont juste une manière légèrement différente d'attacher des propriétés à un objet - vous pouvez facilement fournir les Symbolsméthodes standard bien connues, tout comme celles Object.prototype.hasOwnPropertyqui apparaissent dans tout ce qui hérite Object.

Voici quelques-uns des avantages du Symboltype primitif.

Symbols ont une capacité de débogage intégrée

Symbols peut être donné une description, qui est vraiment juste utilisée pour le débogage pour vous faciliter la vie lorsque vous les connectez à une console.

Symbolspeut être utilisé comme Objectclé

C'est là Symbolque ça devient vraiment intéressant. Ils sont fortement entrelacés avec des objets. Symbolpeuvent être attribués en tant que clés à des objets, ce qui signifie que vous pouvez attribuer un nombre illimité d'uniques Symbolà un objet et être assuré que celles-ci n'entreront jamais en conflit avec des stringclés ou d'autres uniques Symbols.

Symbols peut être utilisé comme une valeur unique.

Supposons que vous avez une bibliothèque d'enregistrement, qui comprend plusieurs niveaux de journaux tels que logger.levels.DEBUG, logger.levels.INFO, logger.levels.WARNet ainsi de suite. Dans le code ES5, vous souhaitez créer ces strings (so logger.levels.DEBUG === 'debug') ou numbers ( logger.levels.DEBUG === 10). Les deux ne sont pas idéales car ces valeurs ne sont pas des valeurs uniques, mais les Symbols le sont! Devient donc logger.levelssimplement:

log.levels = {
  DEBUG: Symbol('debug'),
  INFO: Symbol('info'),
  WARN: Symbol('warn'),
};
log(log.levels.DEBUG, 'debug message');
log(log.levels.INFO, 'info message');

En savoir plus dans cet excellent article .

Mihai Alexandru-Ionut
la source
10
Je ne suis pas sûr de comprendre votre exemple, et pourquoi auriez-vous besoin log.levels = {DEBUG: Symbol('debug')et pas simplement log.levels = {DEBUG:'debug'}. à la fin c'est pareil. Je pense qu'il convient de mentionner que les symboles sont invisibles lors de l'itération sur les clés d'un objet. c'est leur "truc"
vsync
Un avantage est que quelqu'un ne peut pas accidentellement utiliser un littéral et croire que cela fonctionnerait pour toujours. (Notez que ce n'est pas un argument vraiment fort, car on peut simplement utiliser {}et obtenir le même résultat (comme valeur unique), ou peut-être un littéral est préféré dans ce projet, ou vous pouvez dire qu'il faut d'abord lire le doc.) I pense personnellement qu'il offre une bonne lisibilité de sens unique dans le code
apple apple
Remarque lorsqu'il est utilisé comme valeur unique, le littéral d'objet a également un débogage intégré, c'est- à- dire Symbol("some message")devient{message:'some message'} , sans doute, l'objet fait mieux ici car vous pouvez ajouter plusieurs champs.
apple apple
38

Cet article concerne le Symbol(), fourni avec des exemples réels que j'ai pu trouver / faire et des faits et définitions que j'ai pu trouver.

TLDR;

C'est Symbol()le type de données, introduit avec la sortie d'ECMAScript 6 (ES6).

Il y a deux faits curieux sur le symbole.

  • le premier type de données et le seul type de données en JavaScript qui n'a pas de littéral

  • toute variable, définie avec Symbol(), obtient un contenu unique, mais ce n'est pas vraiment privé .

  • toutes les données ont leur propre symbole, et pour les mêmes données, les symboles seraient les mêmes . Plus d'informations dans le paragraphe suivant, sinon ce n'est pas un TLRD; :)

Comment initialiser le symbole?

1. Pour obtenir un identifiant unique avec une valeur débogable

Vous pouvez le faire de cette façon:

var mySymbol1 = Symbol();

Ou de cette façon:

var mySymbol2 = Symbol("some text here");

le "some text here" chaîne ne peut pas être extraite du symbole, c'est juste une description à des fins de débogage. Cela ne change en rien le comportement du symbole. Bien que vous puissiez le console.logfaire (ce qui est juste, car la valeur est pour le débogage, afin de ne pas confondre ce journal avec une autre entrée de journal):

console.log(mySymbol2);
// Symbol(some text here)

2. Pour obtenir un symbole pour certaines données de chaîne

Dans ce cas, la valeur du symbole est réellement prise en compte et de cette façon, deux symboles peuvent être non uniques.

var a1 = Symbol.for("test");
var a2 = Symbol.for("test");
console.log(a1 == a2); //true!

Appelons ces symboles des symboles de "deuxième type". Ils ne se croisent pas avec les symboles de "premier type" (c'est-à-dire ceux définis avecSymbol(data) en aucune façon les ).

Les deux paragraphes suivants ne concernent que le symbole de premier type .

Comment puis-je bénéficier de l'utilisation de Symbol au lieu des anciens types de données?

Prenons d'abord un objet, un type de données standard. Nous pourrions y définir des paires clé-valeur et avoir accès aux valeurs en spécifiant la clé.

var persons = {"peter":"pan","jon":"doe"};
console.log(persons.peter);
// pan

Et si nous avons deux personnes du nom de Peter?

Ce faisant:

var persons = {"peter":"first", "peter":"pan"};

n'aurait pas beaucoup de sens.

Il semble donc que deux personnes absolument différentes aient le même nom. Parlons alors de nouveau Symbol(). C'est comme une personne dans la vraie vie - toute personne est unique , mais leurs noms peuvent être égaux. Définissons deux "personnes".

 var a = Symbol("peter");
 var b = Symbol("peter");

Maintenant, nous avons deux personnes différentes portant le même nom. Nos personnes sont-elles vraiment différentes? Elles sont; vous pouvez vérifier ceci:

 console.log(a == b);
 // false

Comment en bénéficions-nous?

Nous pouvons faire deux entrées dans votre objet pour les différentes personnes et elles ne peuvent en aucun cas être confondues.

 var firstPerson = Symbol("peter");
 var secondPerson = Symbol("peter");
 var persons = {[firstPerson]:"first", [secondPerson]:"pan"};

Remarque:
Il convient de noter, cependant, que la stringification de l'objet JSON.stringifysupprimera toutes les paires initialisées avec un symbole comme clé.
L'exécution Object.keysne retournera pas non plus de telles Symbol()->valuepaires.

En utilisant cette initialisation, il est absolument impossible de confondre les entrées pour les première et deuxième personnes. Les appeler console.logaffichera correctement leurs seconds noms.

 console.log(persons[a]);
 // first
 console.log(persons[b]);
 // pan

Lorsqu'il est utilisé dans un objet, en quoi est-il différent de la définition d'une propriété non énumérable?

En effet, il existait déjà un moyen de définir une propriété à cacher Object.keyset à énumérer. C'est ici:

var anObject = {};
var fruit = "apple";    

Object.defineProperty( anObject, fruit, {
    enumerable: false,
    value: "green"
});

Quelle différence Symbol()y apporte-t-il? La différence est que vous pouvez toujours obtenir la propriété définie avec Object.definePropertyde la manière habituelle:

console.log(anObject[fruit]); //green
console.log(anObject["apple"]); //green
console.log(anObject.apple); //green

Et si défini avec Symbol comme dans le paragraphe précédent:

fruit = Symbol("apple");

Vous n'aurez la possibilité de recevoir sa valeur que si vous connaissez sa variable, c'est-à-dire

console.log(anObject[fruit]); //green
console.log(anObject["apple"]); //undefined
console.log(anObject.apple); //undefined

De plus, la définition d'une autre propriété sous la clé "apple"fera en sorte que l'objet supprime l'ancienne (et s'il est codé en dur, il pourrait générer une erreur). Alors, plus de pommes! C'est dommage. Se référant au paragraphe précédent, les symboles sont uniques et définissent une clé commeSymbol() qui la rendra unique.

Conversion de type et vérification

  • Contrairement à d'autres types de données, il est impossible de convertir le Symbol()en n'importe quel autre type de données.

  • Il est possible de "créer" un symbole basé sur le type de données primitif en appelant Symbol(data).

  • En termes de vérification du type, rien ne change.

    function isSymbol ( variable ) {
        return typeof someSymbol === "symbol";
    }
    
    var a_Symbol = Symbol("hey!");
    var totally_Not_A_Symbol = "hey";
    
    console.log(isSymbol(a_Symbol)); //true
    console.log(isSymbol(totally_Not_A_Symbol)); //false

nicael
la source
Cela a-t-il été migré de la documentation SO?
Knu
1
@KNU ce n'était pas le cas; J'ai rassemblé les informations et écrit cette réponse moi
nicael
Réponse vraiment magnifique!
Mihai Alexandru-Ionut
1
Excellente réponse sur Symbol, mais je ne sais toujours pas pourquoi utiliser un objet avec des clés de symbole au lieu d'un tableau. Si j'ai plusieurs personnes comme {"peter": "pan"} {"john": "doe"} ça me fait mal de les mettre dans un seul objet. Pour la même raison que je ne crée pas de classes avec des propriétés dupliquées comme personFirstName1, personFirstName2. Ceci combiné avec l'incapacité de le strictifier, je ne vois pas les avantages, mais seulement les inconvénients.
eldo
18

Voici comment je le vois. Les symboles offrent «un niveau supplémentaire de confidentialité», en empêchant les clés / propriétés d'un objet d'être exposées via certaines méthodes populaires telles que Object.keys () et JSON.stringify ().

var age = Symbol();  // declared in another module perhaps?
class Person {
   constructor(n,a){
      this.name = n;
      this[age] = a;  
   }
   introduce(){
       console.log(`My name is ${this.name}. I am ${this[age]-10}.`);
   }
}
var j = new Person('Jane',45);
j.introduce();  // My name is Jane. I am 35.
console.log(JSON.stringify(j)); // {"name":"Jane"}
console.log(Object.keys(j)); // ["name"]
console.log(j[age]); // 45   (well…only if you know the age in the first place…)

Bien qu'un objet en soi soit donné, de telles propriétés peuvent toujours être exposées via la réflexion, le proxy, Object.getOwnPropertySymbols (), etc.

Chong Lip Phang
la source
2

Un symbole JS est un nouveau type de données primitif. Ce sont des jetons qui servent d'identifiants uniques . Un symbole peut être créé à l'aide du Symbolconstructeur. Prenez par exemple cet extrait de MDN:

// The symbol constructor takes one optional argument, 
// the descriptions which is used for debugging only.
// Here are two symbols with the same description
let Sym1 = Symbol("Sym");
let Sym2 = Symbol("Sym");
  
console.log(Sym1 == Sym2); // returns "false"
// Symbols are guaranteed to be unique.
// Even if we create many symbols with the same description,
// they are different values.

Il est souvent pratique d'utiliser des symboles comme clés de propriété d'objet uniques, par exemple:

let obj = {};
let prop = Symbol();

obj[prop] = 123;  // the symbol prop is assigned 123
obj.prop  = 456;  // the string prop is assigned 456

console.log(obj.prop, obj[prop]); // logs 456, 123

Willem van der Veen
la source
0

Les symboles ont deux cas d'utilisation principaux:

  1. Propriétés des objets «cachés». Si nous voulons ajouter une propriété à un objet qui «appartient» à un autre script ou à une bibliothèque, nous pouvons créer un symbole et l'utiliser comme clé de propriété. Une propriété symbolique n'apparaît pas dans for..in, elle ne sera donc pas traitée accidentellement avec d'autres propriétés. De plus, il ne sera pas accessible directement, car un autre script n'a pas notre symbole. Ainsi, la propriété sera protégée contre toute utilisation accidentelle ou écrasement.

    Ainsi, nous pouvons «secrètement» cacher quelque chose dans les objets dont nous avons besoin, mais d'autres ne devraient pas le voir, en utilisant des propriétés symboliques.

  2. Il existe de nombreux symboles système utilisés par JavaScript qui sont accessibles en tant que Symbol.*. Nous pouvons les utiliser pour modifier certains comportements intégrés. Par exemple, ...... Symbol.iteratorpour les itérables, Symbol.toPrimitivepour configurer la conversion objet en primitif et ainsi de suite.

La source

snr
la source