web-dev-qa-db-fra.com

Quelle est la motivation pour amener les symboles dans ES6?

UPDATE: Récemment, un article brillant de Mozilla est apparu. Lisez-le si vous êtes curieux.

Comme vous le savez peut-être, ils sont prévoient d'inclure le nouveau type de primitive Symbol dans ECMAScript 6 (sans oublier quelques autres trucs fous). J'ai toujours pensé que la notion de :symbol dans 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 réellement besoin de symboles en JavaScript?

336
Yanis

La motivation initiale pour l'introduction de symboles dans Javascript était d'activer les propriétés privées.

Malheureusement, ils ont fini par être sévèrement dégradés. Ils ne sont plus privés, car vous pouvez les trouver via la réflexion, par exemple, en utilisant Object.getOwnPropertySymbols ou des proxies.

Ils sont maintenant appelés symboles niques et leur seule utilisation est d'éviter les conflits de noms entre propriétés. Par exemple, ECMAScript lui-même peut désormais introduire des points d’extension via certaines méthodes que vous pouvez appliquer à des objets (par exemple, pour définir leur protocole d’itération) sans risquer de les heurter avec des noms d’utilisateur.

La motivation pour ajouter des symboles à la langue est assez discutable.

211
Andreas Rossberg

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 Symbol pour 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 propriété de classe Pettype n'est pas privée. Pour le rendre privé, nous devons créer une fermeture. L'exemple ci-dessous montre comment rendre type privé 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 instance Pet créée, ce qui peut nuire aux performances.

Maintenant nous introduisons Symbol. Cela peut nous aider à rendre une propriété privée sans utiliser de fermetures inutiles. 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
89
Samar Panda

Symbols est un nouveau type d'objet spécial qui peut être utilisé comme nom de propriété unique dans les objets. Utiliser Symbol au lieu de string permet à différents modules de créer des propriétés qui ne sont pas en conflit les unes avec les autres. Symbols peut également être rendu privé, de sorte que leurs propriétés ne sont pas accessibles aux personnes n'ayant pas déjà un accès direct à Symbol.

Symbols sont une nouvelle primitive . Tout comme les primitives number, string et boolean, Symbol ont une fonction qui peut être utilisée pour les créer. Contrairement aux autres primitives, Symbols n'a pas de syntaxe littérale (par exemple, comment string ont '') - le seul moyen de les créer consiste à utiliser le constructeur Symbol de la manière suivante:

let symbol = Symbol();

En réalité, Symbol n'est qu'une manière légèrement différente d'attacher des propriétés à un objet - vous pouvez facilement fournir le nom connu Symbols en tant que méthodes standard, tout comme Object.prototype.hasOwnProperty qui figure dans tout ce qui hérite de Object.

Voici certains des avantages du type primitif Symbol.

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

Il est possible de donner à Symbols une description, qui est en réalité simplement utilisée pour le débogage afin de faciliter la vie lorsque vous les enregistrez sur une console.

Symbols peut être utilisé comme Object

C'est là que Symbol devient vraiment intéressant. Ils sont fortement liés aux objets. Symbol peut être affecté en tant que clés à des objets, ce qui signifie que vous pouvez affecter un nombre illimité de Symbol uniques à un objet et être assuré que ceux-ci ne seront jamais en conflit avec string ou à un autre Symbols unique.

Symbols peut être utilisé comme valeur unique.

Supposons que vous disposiez d’une bibliothèque de journalisation comprenant plusieurs niveaux de journalisation, tels que logger.levels.DEBUG, logger.levels.INFO, logger.levels.WARN, etc. Dans le code ES5, vous voudriez créer ces strings (donc logger.levels.DEBUG === 'debug'), ou numbers (logger.levels.DEBUG === 10). Ces deux valeurs ne sont pas idéales car ces valeurs ne sont pas des valeurs uniques, mais Symbols le sont! Donc, logger.levels devient simplement:

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

Lire la suite dans ce excellent article.

35

Cet article concerne la Symbol(), avec des exemples concrets que je pourrais trouver/faire et des faits et définitions que je pourrais trouver.

TLDR;

La Symbol() est le type de données introduit avec la version ECMAScript 6 (ES6).

Il y a deux faits curieux sur le symbole.

  • le premier type de données et 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é .

  • toute donnée a son symbole propre , et pour les mêmes données, les symboles seraient identiques . 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 de débogage

Vous pouvez le faire soit de cette façon:

var mySymbol1 = Symbol();

Ou de cette façon:

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

La chaîne "some text here" ne peut pas être extraite du symbole, il s'agit simplement d'une description à des fins de débogage. Cela ne change en rien le comportement du symbole. Bien que vous puissiez console.log le (ce qui est juste, puisque 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 ainsi 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 second type". Ils ne se croisent pas avec les symboles du "premier type" (c'est-à-dire ceux définis avec Symbol(data)) de quelque manière que ce soit.

Les deux paragraphes suivants concernent uniquement le symbole de premier type .

Quels sont les avantages d'utiliser Symbol au lieu des types de données plus anciens?

Considérons d’abord un objet, un type de données standard. Nous pourrions définir certaines paires clé-valeur et avoir un 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 avec le nom de Peter?

Ce faisant:

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

ça n'aurait pas beaucoup de sens.

Donc, semble être un problème de deux personnes absolument différentes ayant le même nom. Faisons ensuite référence à la nouvelle Symbol(). C'est comme une personne dans la vie réelle - chaque personne est unique , mais leurs noms peuvent être égaux. Définissons deux "personnes".

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

Nous avons maintenant 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 profitons-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:
Toutefois, il convient de noter que le fait de chaîner l'objet avec JSON.stringify supprimera toutes les paires initialisées avec un symbole comme clé.
L'exécution de Object.keys ne renverra pas non plus de telles paires Symbol()->value.

Avec cette initialisation, il est absolument impossible de confondre les entrées pour la première et la deuxième personne. Si vous appelez console.log pour eux, leur deuxième nom sera correctement affiché.

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

Lorsqu'il est utilisé dans object, 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é à masquer de Object.keys et d’é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.defineProperty de 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 ne pourrez recevoir sa valeur que si vous connaissez sa variable, c.-à-d.

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 que l’objet supprime l’ancienne (et, s’il est codé en dur, il pourrait générer une erreur). Donc, plus de pommes! C'est dommage. En se référant au paragraphe précédent, les symboles sont uniques et définir une clé comme Symbol() le rendra unique.

Conversion de type et vérification

  • Contrairement aux autres types de données, il est impossible de convertir Symbol() en un autre type de données.

  • Il est possible de "créer" un symbole basé sur un 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
    

34
nicael

Voici comment je le vois. Les symboles fournissent un "niveau supplémentaire de confidentialité" en empêchant les clés/propriétés d'un objet d'être exposées par le biais de méthodes courantes 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’il soit donné à un objet en soi, de telles propriétés peuvent toujours être exposées par réflexion, proxy, Object.getOwnPropertySymbols (), etc., il n’existe aucun moyen naturel d’y accéder par quelques méthodes directes, qui peuvent parfois suffire à partir de OOP perspective.

17
Chong Lip Phang

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 constructeur Symbol. 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 utile d’utiliser des symboles en tant que clés uniques de propriétés d’objet, 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
0
Willem van der Veen