428 Stimmen

Was ist die Motivation, Symbole nach ES6 zu bringen?

UPDATE: Vor kurzem wurde ein brillanter Artikel von Mozilla veröffentlicht. Lesen Sie ihn, wenn Sie neugierig sind.

Wie Sie vielleicht wissen, planen sie, einen neuen Symbol-Primitivtyp in ECMAScript 6 aufzunehmen (um nicht zu erwähnen, einige andere verrückte Sachen). Ich habe immer gedacht, dass die :symbol -Notation in Ruby unnötig ist; wir könnten problemlos einfache Zeichenfolgen verwenden, wie wir es in JavaScript tun. Und jetzt entscheiden sie sich, die Dinge in JS mit diesem Symbol zu komplizieren.

Ich verstehe die Motivation nicht. Könnte mir jemand erklären, ob wir wirklich Symbole in JavaScript brauchen?

264voto

Andreas Rossberg Punkte 32540

Die ursprüngliche Motivation zur Einführung von Symbolen in Javascript war es, private Eigenschaften zu ermöglichen.

Leider wurden sie stark abgewertet. Sie sind nicht mehr privat, da sie beispielsweise mithilfe von Object.getOwnPropertySymbols oder Proxies über Reflexion gefunden werden können.

Sie sind nun als einzigartige Symbole bekannt und ihr einziger beabsichtigter Verwendungszweck besteht darin, Namenskonflikte zwischen Eigenschaften zu vermeiden. Zum Beispiel kann ECMAScript nun über bestimmte Methoden Erweiterungshaken einführen, die Sie auf Objekte setzen können (zum Beispiel, um ihr Iterationsprotokoll zu definieren), ohne dass diese mit Benutzernamen kollidieren.

Ob das stark genug ist, um Symbole zur Sprache hinzuzufügen, ist umstritten.

125voto

Samar Panda Punkte 3856

Symbole garantieren keine echte Privatsphäre, können jedoch verwendet werden, um öffentliche und interne Eigenschaften von Objekten zu trennen. Betrachten wir ein Beispiel, in dem wir Symbol verwenden können, um private Eigenschaften zu haben.

Betrachten wir ein Beispiel, in dem eine Eigenschaft eines Objekts nicht privat ist.

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

var a = new Pet('Hund');
console.log(a.getType());//Ausgabe: Hund
a.type = null;
//Außerhalb geändert
console.log(a.getType());//Ausgabe: null

Oben ist die Eigenschaft type der Klasse Pet nicht privat. Um sie privat zu machen, müssen wir eine Closure erstellen. Das folgende Beispiel zeigt, wie wir type mithilfe einer Closure privat machen können.

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

var b = new Pet('Hund');
console.log(b.getType());//Hund
b.type = null;
//Bleibt privat
console.log(b.getType());//Hund

Nachteil des oben genannten Ansatzes: Wir führen für jede erstellte Instanz von Pet eine zusätzliche Closure ein, was die Leistung beeinträchtigen kann.

Jetzt führen wir Symbol ein. Dies kann uns helfen, eine Eigenschaft privat zu machen, ohne zusätzliche, unnötige Closures zu verwenden. Beispielcode unten:

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

var a = new Pet('Hund');
console.log(a.getType());//Ausgabe: Hund
a.type = null;
//Bleibt privat
console.log(a.getType());//Ausgabe: Hund

57voto

nicael Punkte 17457

Dieser Beitrag handelt von Symbol(), mit tatsächlichen Beispielen, die ich finden/erstellen konnte, und Fakten & Definitionen, die ich finden konnte.

TLDR;

Das Symbol() ist der Datentyp, der mit der Veröffentlichung von ECMAScript 6 (ES6) eingeführt wurde.

Es gibt zwei interessante Fakten über das Symbol.

  • der erste Datentyp und einzige Datentyp in JavaScript, der kein Literal hat

  • irgendwelche Variablen, die mit Symbol() definiert sind, erhalten einen eindeutigen Inhalt, aber es ist nicht wirklich privat.

  • irgendeine Daten haben ihr eigenes Symbol, und für die gleichen Daten wären die Symbole gleich. Weitere Informationen im nächsten Absatz, sonst ist es kein TLDR; :)

Wie initialisiere ich das Symbol?

1. Um eine eindeutige Kennung mit einem debugbaren Wert zu erhalten

Sie können es auf diese Weise tun:

var mySymbol1 = Symbol();

Oder auf diese Weise:

var mySymbol2 = Symbol("etwas Text hier");

Der String "etwas Text hier" kann nicht aus dem Symbol extrahiert werden, es ist nur eine Beschreibung für Debugging-Zwecke. Es ändert das Verhalten des Symbols jedoch nicht. Allerdings könnten Sie es console.log (was fair ist, da der Wert zum Debuggen ist, um diesen Log nicht mit einem anderen Logeintrag zu verwechseln):

console.log(mySymbol2);
// Symbol(etwas Text hier)

2. Um ein Symbol für einige Zeichendaten zu erhalten

In diesem Fall wird der Wert des Symbols tatsächlich berücksichtigt, und auf diese Weise können zwei Symbole nicht eindeutig sein.

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

Nennen wir diese Symbole "Symbole des zweiten Typs". Sie haben keinen Zusammenhang mit den "Symbole des ersten Typs" (d.h. die mit Symbol(data) definierten) in irgendeiner Weise.

Die nächsten beiden Absätze betreffen nur das Symbol des ersten Typs.

Wie profitiere ich von der Verwendung von Symbol anstelle der älteren Datentypen?

Lassen Sie uns zunächst ein Objekt betrachten, einen Standard-Datentyp. Wir könnten dort einige Schlüssel-Wert-Paare definieren und auf die Werte zugreifen, indem wir den Schlüssel angeben.

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

Was ist, wenn wir zwei Personen mit dem Namen Peter haben?

Wenn wir dies tun:

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

würde nicht viel Sinn ergeben.

Es scheint also ein Problem zu sein, wenn zwei völlig unterschiedliche Personen denselben Namen haben. Lassen Sie uns dann auf unser neues Symbol() verweisen. Es ist wie eine Person im wirklichen Leben - jede Person ist einzigartig, aber ihre Namen können gleich sein. Lassen Sie uns zwei "Personen" definieren.

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

Jetzt haben wir zwei verschiedene Personen mit demselben Namen. Sind unsere Personen wirklich unterschiedlich? Sie sind; Sie können dies überprüfen:

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

Wie profitieren wir davon?

Wir können zwei Einträge in Ihrem Objekt für die verschiedenen Personen machen, und sie können in keiner Weise verwechselt werden.

 var erstePerson = Symbol("peter");
 var zweitePerson = Symbol("peter");
 var personen = {[erstePerson]:"first", [zweitePerson]:"pan"};

Hinweis:
Es ist jedoch erwähnenswert, dass das Umwandeln des Objekts mit JSON.stringify alle mit einem Symbol initialisierten Paare mit einem Schlüssel Symbol()->Wert entfernen wird.
Die Ausführung von Object.keys wird ebenfalls keine solchen Symbol()->Wert Paare zurückgeben.

Bei dieser Initialisierung ist es absolut unmöglich, die Einträge für die erste und zweite Person zu verwechseln. Wenn Sie console.log für sie aufrufen, werden ihre Nachnamen korrekt ausgegeben.

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

Wenn es in einem Objekt verwendet wird, wie unterscheidet es sich im Vergleich zur Definition einer nicht-aufzählbaren Eigenschaft?

In der Tat gab es bereits eine Möglichkeit, eine Eigenschaft zu definieren, die vor Object.keys und Enumeration verborgen ist. Hier ist es:

var einObjekt = {};
var obst = "apfel";    

Object.defineProperty( einObjekt, obst, {
    enumerable: false,
    value: "grün"
});

Was bringt das Symbol() dort mit? Der Unterschied besteht darin, dass Sie die mit Object.defineProperty definierte Eigenschaft immer auf übliche Weise erhalten können:

console.log(einObjekt[obst]); //grün
console.log(einObjekt["apfel"]); //grün
console.log(einObjekt.apfel); //grün

Und wenn wie im vorherigen Absatz mit Symbol definiert:

obst = Symbol("apfel");

Sie können den Wert nur erhalten, wenn Sie seine Variable kennen, d.h.

console.log(einObjekt[obst]); //grün
console.log(einObjekt["apfel"]); //undefined
console.log(einObjekt.apfel); //undefined

Darüber hinaus wird das Definieren einer weiteren Eigenschaft unter dem Schlüssel "apfel" dazu führen, dass das Objekt die ältere verliert (und wenn fest codiert, könnte es einen Fehler werfen). Also, keine Äpfel mehr! Das ist schade. Bezugnehmend auf den vorherigen Absatz sind die Symbole einzigartig, und das Definieren eines Schlüssels als Symbol() macht ihn einzigartig.

Typumwandlung und -überprüfung

  • Im Gegensatz zu anderen Datentypen ist es unmöglich, das Symbol() in einen anderen Datentyp umzuwandeln.

  • Es ist möglich, ein Symbol auf der Grundlage eines primitiven Datentyps durch Aufrufen von Symbol(data) zu "erstellen".

  • In Bezug auf die Überprüfung des Typs ändert sich nichts.

     function isSymbol ( variable ) {
         return typeof someSymbol === "symbol";
     }
    
     var ein_Symbol = Symbol("hallo!");
     var überhaupt_Nicht_Ein_Symbol = "hallo";
    
     console.log(isSymbol(ein_Symbol)); //true
     console.log(isSymbol(überhaupt_Nicht_Ein_Symbol)); //false

52voto

Symbole sind eine neue, spezielle Art von Objekt, die als eindeutiger Eigenschaftsname in Objekten verwendet werden können. Die Verwendung von Symbolen anstelle von Strings ermöglicht es verschiedenen Modulen, Eigenschaften zu erstellen, die nicht miteinander in Konflikt geraten. Symbole können auch effektiv privat gemacht werden, sodass auf ihre Eigenschaften nur zugegriffen werden kann, wenn man bereits direkten Zugriff auf das Symbol hat.

Symbole sind ein neues Primitivum, genau wie die Primitiva Zahl, String und Boolean. Im Gegensatz zu den anderen Primitiva haben Symbole keine Literal-Syntax (z.B. wie String '') - sie können nur mithilfe des Symbol-Konstruktors auf folgende Weise erstellt werden:

let symbol = Symbol();

Im Grunde genommen sind Symbole nur eine etwas andere Art, Eigenschaften an ein Objekt anzuhängen - man könnte problemlos die bekannten Symbole als Standardmethoden bereitstellen, genau wie Object.prototype.hasOwnProperty, das in allem erscheint, was von Object erbt.

Hier sind einige Vorteile des Symbol-Primitivtyps.

Symbole haben Debugbarkeit integriert

Symbole können eine Beschreibung erhalten, die tatsächlich nur zur Verbesserung der Debugging-Funktion dient, um das Leben beim Protokollieren in die Konsole etwas einfacher zu machen.

Symbole können als Objektschlüssel verwendet werden

Hier werden Symbole wirklich interessant. Sie sind eng mit Objekten verflochten. Symbole können als Schlüssel für Objekte zugewiesen werden, was bedeutet, dass Sie einer unbegrenzten Anzahl von eindeutigen Symbolen ein Objekt zuweisen können und garantieren können, dass diese niemals mit String-Schlüsseln oder anderen eindeutigen Symbolen in Konflikt geraten werden.

Symbole können als eindeutige Werte verwendet werden

Angenommen, Sie haben eine Protokollierungsbibliothek, die verschiedene Protokollierungsstufen wie logger.levels.DEBUG, logger.levels.INFO, logger.levels.WARN usw. enthält. Im ES5-Code möchten Sie diese als Strings (also logger.levels.DEBUG === 'debug') oder Zahlen (logger.levels.DEBUG === 10) definieren. Beides ist nicht ideal, da diese Werte keine eindeutigen Werte sind, aber Symbole sind es! Also wird logger.levels einfach zu:

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

Erfahre mehr in diesem großartigen Artikel.

20voto

Chong Lip Phang Punkte 8249

Hier ist, wie ich es sehe. Symbole bieten 'eine zusätzliche Ebene der Privatsphäre', indem sie verhindern, dass die Schlüssel/Eigenschaften eines Objekts durch einige beliebte Methoden wie Object.keys() und JSON.stringify() offengelegt werden.

var alter = Symbol();  // vielleicht in einem anderen Modul deklariert?
class Person {
   constructor(n,a){
      this.name = n;
      this[alter] = a;  
   }
   vorstellen(){
       console.log(`Mein Name ist ${this.name}. Ich bin ${this[alter]-10}.`);
   }
}
var j = new Person('Jane',45);
j.vorstellen();  // Mein Name ist Jane. Ich bin 35.
console.log(JSON.stringify(j)); // {"name":"Jane"}
console.log(Object.keys(j)); // ["name"]
console.log(j[alter]); // 45   (nun ja... nur wenn man das Alter überhaupt weiß...)

Auch wenn bei einem Objekt an sich solche Eigenschaften durch Reflexion, Proxy, Object.getOwnPropertySymbols() usw. immer noch offengelegt werden können, gibt es keine natürlichen Mittel, um sie durch einige direkte Methoden zuzugreifen, was aus einer OOP-Perspektive manchmal ausreichend sein kann.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X