Was ist die Motivation, Symbole auf ES6 zu bringen?


368

UPDATE : Kürzlich erschien ein brillanter Artikel von Mozilla . Lesen Sie es, wenn Sie neugierig sind.

Wie Sie vielleicht wissen, planen sie, einen neuen primitiven Symboltyp in ECMAScript 6 aufzunehmen (ganz zu schweigen von einigen anderen verrückten Dingen). Ich habe immer gedacht, dass der :symbolBegriff in Ruby unnötig ist; Wir könnten stattdessen leicht einfache Zeichenfolgen verwenden, wie wir es in JavaScript tun. Und jetzt beschließen sie, die Dinge in JS damit zu komplizieren.

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


6
Ich weiß nicht, wie authentisch diese Erklärung ist, aber es ist ein Anfang: tc39wiki.calculist.org/es6/symbols .
Felix Kling

8
Symbole ermöglichen so viel , dass sie eindeutige Bezeichner für Objekte mit Gültigkeitsbereich ermöglichen. Zum Beispiel Eigenschaften für Objekte, auf die nur an einer Stelle zugegriffen werden kann.
Benjamin Gruenbaum

5
Ich bin mir nicht sicher, da Sie Object.getOwnPropertySymbols (o)
Yanis

4
Es ist eher Einzigartigkeit als Privatsphäre.
Qantas 94 Heavy

2
Sie wollten eine kompliziertere Klassenimplementierung haben , mit privateund publicKlassenattribut Schlüsselwort , dass sie beschlossen , für eine einfachere Implementierung Klasse zu Graben. Anstelle von this.x = xdir sollte public x = xund für private Variablen private y = y. Sie beschlossen, dies für eine wesentlich minimalere Klassenimplementierung aufzugeben. Das Symbol wäre dann eine erforderliche Problemumgehung, um private Eigenschaften in der minimalen Implementierung zu erhalten.
Lyschoening

Antworten:


224

Die ursprüngliche Motivation für die Einführung von Symbolen in Javascript bestand darin, private Immobilien zu ermöglichen .

Leider wurden sie stark herabgestuft. Sie sind nicht mehr privat, da Sie sie beispielsweise durch Reflektion finden könnenObject.getOwnPropertySymbols oder Proxys finden können.

Sie sind jetzt als einzigartig bekannt Symbole bezeichnet und dienen nur dazu, Namenskonflikte zwischen Eigenschaften zu vermeiden. Beispielsweise kann ECMAScript selbst jetzt Erweiterungs-Hooks über bestimmte Methoden einführen, die Sie Objekten zuweisen können (z. B. um deren Iterationsprotokoll zu definieren), ohne dass das Risiko besteht, dass sie mit Benutzernamen in Konflikt geraten.

Ob dies stark genug ist, um der Sprache Symbole hinzuzufügen, ist umstritten.


93
Die meisten Sprachen (alle gängigen Sprachen afaik) bieten einen Mechanismus, normalerweise Reflexion, um trotzdem Zugang zu privat zu erhalten.
Esailija

19
@Esailija, ich denke nicht, dass das stimmt - insbesondere, da viele Sprachen überhaupt keine Reflexion bieten. Das Durchsickern des privaten Zustands durch Reflexion (wie z. B. in Java) sollte als Fehler und nicht als Merkmal betrachtet werden. Dies gilt insbesondere für Webseiten, auf denen ein zuverlässiger privater Status sicherheitsrelevant sein kann. Derzeit ist die einzige Möglichkeit, dies in JS zu erreichen, die Schließung, die sowohl langwierig als auch kostspielig sein kann.
Andreas Rossberg

38
Der Mechanismus muss nicht reflektiert werden - C ++, Java, C #, Ruby, Python, PHP und Objective-C ermöglichen den Zugriff auf die eine oder andere Weise, wenn man es wirklich will. Es geht nicht wirklich um Können, sondern um Kommunikation.
Esailija

4
@plalx, ​​im Web geht es bei der Kapselung manchmal auch um Sicherheit.
Andreas Rossberg

3
@ RolandPihlakas ist leider Object.getOwnPropertySymbolsnicht das einzige Leck; Schwieriger ist die Möglichkeit, mithilfe von Proxys den Zugriff auf eine "private" Eigenschaft abzufangen.
Andreas Rossberg

95

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

Nehmen 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('dog');
console.log(a.getType());//Output: dog
a.type = null;
//Modified outside
console.log(a.getType());//Output: null

Oben ist die PetKlasseneigenschaft typenicht privat. Um es privat zu machen, müssen wir einen Verschluss erstellen. Das folgende Beispiel zeigt, wie wir typemit einem Verschluss privat machen können .

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

Nachteil des obigen Ansatzes: Wir führen für jede Peterstellte Instanz einen zusätzlichen Abschluss ein , der die Leistung beeinträchtigen kann.

Jetzt stellen wir vor Symbol. Dies kann uns helfen, eine Immobilie privat zu machen, ohne zusätzliche unnötige Schließungen zu verwenden. Codebeispiel unten:

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

15
Beachten Sie, dass Symboleigenschaften nicht privat sind ! Symbole sind kollisionsfrei . Möglicherweise möchten Sie die akzeptierte Antwort lesen.
Bergi

3
Ja, das Symbol garantiert keine echte Privatsphäre, kann jedoch verwendet werden, um öffentliche und interne Eigenschaften von Objekten zu trennen. Entschuldigung, ich habe vergessen, diesen Punkt zu meiner Antwort hinzuzufügen. Wird meine Antwort entsprechend aktualisieren.
Samar Panda

@SamarPanda, Sie können genauso gut sagen, dass das Präfixieren von Mitgliedern mit _keine echte Privatsphäre garantiert, sondern dazu verwendet werden kann, öffentliche und interne Eigenschaften von Objekten zu trennen. Mit anderen Worten, sinnlose Antwort.
Pacerier

10
Ich würde nicht sagen, dass es sinnlos ist, da Symbole standardmäßig nicht aufzählbar sind und auch nicht versehentlich darauf zugegriffen werden kann, während dies bei jedem anderen Schlüssel der Fall ist.
Patrick

5
Ich finde Ihre Antwort die einzige, die tatsächlich ein sinnvolles Beispiel dafür hat, warum Sie das private Attribut des Objekts als Symbol anstatt nur als normales Attribut definieren möchten.
Luis Lobo Borobia

42

Symbolssind eine neue, spezielle Art von Objekt, das als eindeutiger Eigenschaftsname in Objekten verwendet werden kann. Durch die Verwendung von Symbolanstelle von string's können verschiedene Module Eigenschaften erstellen, die nicht miteinander in Konflikt stehen. Symbolskann auch privat gemacht werden, so dass niemand auf seine Eigenschaften zugreifen kann, der noch keinen direkten Zugriff auf die hat Symbol.

Symbolssind ein neues Primitiv . Genau wie die number, stringund booleanPrimitiven Symbolhaben sie eine Funktion, mit der sie erstellt werden können. SymbolsHaben Sie im Gegensatz zu den anderen Grundelementen keine wörtliche Syntax (z. B. wie stringhaben '') - die einzige Möglichkeit, sie zu erstellen, besteht darin, den SymbolKonstruktor folgendermaßen zu verwenden:

let symbol = Symbol();

In Wirklichkeit ist Symboles nur eine etwas andere Art, Eigenschaften an ein Objekt anzuhängen - Sie könnten leicht die bekannten SymbolsStandardmethoden bereitstellen , wie sie Object.prototype.hasOwnPropertyin allem, was von ihnen erbt, vorkommen Object.

Hier sind einige der Vorteile des Symbolprimitiven Typs.

Symbols Debuggabilität eingebaut haben

Symbols kann eine Beschreibung gegeben werden, die wirklich nur zum Debuggen verwendet wird, um das Leben ein wenig einfacher zu machen, wenn sie an einer Konsole protokolliert werden.

Symbolskann als ObjectSchlüssel verwendet werden

Hier wird Symboles wirklich interessant. Sie sind stark mit Objekten verflochten. Symbolkann als Schlüssel für Objekte zugewiesen werden, dh Sie können Symboleinem Objekt eine unbegrenzte Anzahl von eindeutigen Elementen zuweisen und sicherstellen, dass diese niemals mit stringSchlüsseln oder anderen eindeutigen Schlüsseln in Konflikt stehen Symbols.

Symbols kann als eindeutiger Wert verwendet werden.

Nehmen wir an , Sie eine Logging - Bibliothek, die mehrere Protokollebenen wie beinhaltet logger.levels.DEBUG, logger.levels.INFO, logger.levels.WARNund so weiter. Im ES5-Code möchten Sie diese strings (so logger.levels.DEBUG === 'debug') oder numbers ( logger.levels.DEBUG === 10) erstellen . Beide sind nicht ideal, da diese Werte keine eindeutigen Werte sind, aber Symbols! So wird logger.levelseinfach:

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

Lesen Sie mehr in diesem großartigen Artikel .


10
Ich bin mir nicht sicher, ob ich Ihr Beispiel verstehe und warum Sie es brauchen würden log.levels = {DEBUG: Symbol('debug')und nicht einfach log.levels = {DEBUG:'debug'}. am Ende ist es das gleiche. Ich denke, es ist erwähnenswert, dass Symbole unsichtbar sind, wenn über die Schlüssel eines Objekts iteriert wird. das ist ihr "Ding"
vsync

Ein Vorteil ist, dass jemand nicht versehentlich ein Literal verwenden kann und glaubt, dass es für immer funktionieren würde. (Beachten Sie, dass dies kein wirklich starkes Argument ist, da man einfach {}dasselbe Ergebnis verwenden und erzielen kann (als eindeutiger Wert) oder vielleicht ein Literal in diesem Projekt bevorzugt wird, oder man kann sagen, dass man zuerst das Dokument lesen muss.) I. Ich persönlich denke, es bietet eine gute Lesbarkeit von einzigartiger Bedeutung im Code
Apple Apple

Beachten Sie, dass Objektliteral bei Verwendung als eindeutiger Wert auch eine eingebaute Debugbarkeit aufweist, dh Symbol("some message")wird{message:'some message'} , wohl Objekt tut besser hier , wie Sie mehr Feld hinzufügen können.
Apfel Apfel

38

In diesem Beitrag geht es um die Symbol() , mit tatsächlichen Beispielen, die ich finden / machen konnte, und Fakten und Definitionen, die ich finden konnte.

TLDR;

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

Es gibt zwei merkwürdige Fakten über das Symbol.

  • Der erste Datentyp und der einzige Datentyp in JavaScript, der kein Literal enthält

  • Jede Variable, die mit definiert ist Symbol(), erhält eindeutigen Inhalt, ist jedoch nicht wirklich privat .

  • Alle Daten haben ein eigenes Symbol, und für dieselben Daten wären die Symbole dieselben . Weitere Informationen im folgenden Absatz, ansonsten handelt es sich nicht um eine TLRD. :) :)

Wie initialisiere ich das Symbol?

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

Sie können es entweder so machen:

var mySymbol1 = Symbol();

Oder so:

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

Das "some text here" Zeichenfolge kann nicht aus dem Symbol extrahiert werden, sondern ist nur eine Beschreibung für Debugging-Zwecke. Es ändert in keiner Weise das Verhalten des Symbols. Obwohl Sie es könnten console.log(was fair ist, da der Wert für das Debuggen ist, um dieses Protokoll nicht mit einem anderen Protokolleintrag zu verwechseln):

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

2. Um ein Symbol für einige Zeichenfolgendaten 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 vom zweiten Typ". Sie überschneiden sich nicht mit den Symbolen des "ersten Typs" (dh denjenigen, die mit definiert sindSymbol(data) in keiner Weise ).

Die nächsten beiden Absätze beziehen sich nur auf die Symbol ersten Typ .

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

Betrachten wir zunächst ein Objekt, einen Standarddatentyp. Wir könnten dort einige Schlüssel-Wert-Paare definieren und durch Angabe des Schlüssels auf die Werte zugreifen.

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

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

Dies tun:

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

würde nicht viel Sinn machen.

Es scheint also ein Problem von zwei absolut unterschiedlichen Personen mit demselben Namen zu sein. Lassen Sie uns dann neu verweisen Symbol(). Es ist wie eine Person im wirklichen Leben - jede Person ist einzigartig , aber ihre Namen können gleich sein. Definieren wir zwei "Personen".

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

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

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

Wie profitieren wir dort?

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

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

Hinweis:
Es ist jedoch zu beachten, dass beim Stringing des Objekts JSON.stringifyalle Paare gelöscht werden, die mit einem Symbol als Schlüssel initialisiert wurden.
Beim Ausführen Object.keyswerden solche Symbol()->valuePaare auch nicht zurückgegeben.

Mit dieser Initialisierung ist es absolut unmöglich, die Einträge mit der ersten und zweiten Person zu verwechseln. Wenn Sie console.lognach ihnen rufen, werden ihre zweiten Namen korrekt ausgegeben.

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

Wie unterscheidet es sich bei der Verwendung in Objekten von der Definition nicht aufzählbarer Eigenschaften?

In der Tat gab es bereits eine Möglichkeit, eine zu versteckende Eigenschaft Object.keysund eine Aufzählung zu definieren . Hier ist es:

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

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

Welchen Unterschied Symbol()bringt es dort? Der Unterschied besteht darin, dass Sie die Eigenschaft weiterhin Object.definePropertyauf die übliche Weise definieren können:

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

Und wenn mit Symbol wie im vorherigen Absatz definiert:

fruit = Symbol("apple");

Sie können den Wert nur erhalten, wenn Sie die Variable kennen, d. H.

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

"apple"Wenn Sie eine andere Eigenschaft unter dem Schlüssel definieren, wird das ältere Objekt gelöscht (und wenn es fest codiert ist, kann es einen Fehler auslösen). Also keine Äpfel mehr! Das ist schade. Unter Bezugnahme auf den vorherigen Absatz sind die Symbole eindeutig und definieren einen Schlüssel Symbol(), der ihn eindeutig macht.

Typkonvertierung und -prüfung

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

  • Es ist möglich, ein Symbol basierend auf dem primitiven Datentyp durch Aufrufen zu "erstellen" Symbol(data).

  • An der Überprüfung des Typs ändert sich nichts.

    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


Wurde dies aus der SO-Dokumentation migriert?
Knu

1
@KNU war es nicht; Ich habe die Informationen gesammelt und diese Antwort selbst geschrieben
Nicael

Wirklich schöne Antwort!
Mihai Alexandru-Ionut

1
Tolle Antwort auf Symbol, aber ich weiß immer noch nicht, warum ich ein Objekt mit Symbolschlüsseln anstelle eines Arrays verwenden sollte. Wenn ich mehrere Personen wie {"peter": "pan"} {"john": "doe"} habe, fühlt es sich schlecht für mich an, sie in ein Objekt zu setzen. Aus dem gleichen Grund, aus dem ich keine Klassen mit doppelten Eigenschaften wie personFirstName1, personFirstName2 erstelle. In Verbindung mit der Unfähigkeit, es zu kennzeichnen, sehe ich nicht nur Vorteile, sondern auch Nachteile.
Eldo

18

So sehe ich das. Symbole bieten ein zusätzliches Maß an Datenschutz, indem sie verhindern, dass die Schlüssel / Eigenschaften eines Objekts durch einige gängige Methoden wie Object.keys () und JSON.stringify () verfügbar gemacht werden.

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…)

Obwohl ein Objekt an sich gegeben ist, können solche Eigenschaften dennoch durch Reflection, Proxy, Object.getOwnPropertySymbols () usw. verfügbar gemacht werden. Es gibt jedoch keine natürlichen Mittel, um über einige direkte Methoden auf sie zuzugreifen, was manchmal aus OOP-Sicht ausreichend sein kann.


2

Ein JS-Symbol ist ein neuer primitiver Datentyp. Dies sind Token, die als eindeutige IDs dienen . Mit dem SymbolKonstruktor kann ein Symbol erstellt werden . Nehmen Sie zum Beispiel diesen Ausschnitt aus 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.

Es ist oft praktisch, Symbole als eindeutige Objekteigenschaftsschlüssel zu verwenden, zum Beispiel:

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

Symbole haben zwei Hauptanwendungsfälle:

  1. "Versteckte" Objekteigenschaften. Wenn wir einem Objekt, das zu einem anderen Skript oder einer Bibliothek gehört, eine Eigenschaft hinzufügen möchten, können wir ein Symbol erstellen und es als Eigenschaftsschlüssel verwenden. Eine symbolische Eigenschaft wird in nicht angezeigtfor..in , sodass sie nicht versehentlich zusammen mit anderen Eigenschaften verarbeitet wird. Es wird auch nicht direkt darauf zugegriffen, da ein anderes Skript unser Symbol nicht hat. So wird das Eigentum vor versehentlichem Gebrauch oder Überschreiben geschützt.

    So können wir etwas „verdeckt“ in Objekten verstecken, die wir brauchen, aber andere sollten es nicht sehen, indem wir symbolische Eigenschaften verwenden.

  2. Es gibt viele von JavaScript verwendete Systemsymbole, auf die als zugegriffen werden kann Symbol.*. Wir können sie verwenden, um einige integrierte Verhaltensweisen zu ändern. Zum Beispiel ...... Symbol.iteratorfür iterables, Symbol.toPrimitiveum die Konvertierung von Objekt zu Primitiv einzurichten und so weiter.

Quelle

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.