Qual è la motivazione per portare Symbols su ES6?


368

AGGIORNAMENTO : Di recente è uscito un brillante articolo di Mozilla . Leggilo se sei curioso.

Come forse saprai, stanno pianificando di includere un nuovo tipo di primitiva Symbol in ECMAScript 6 (per non parlare di altre cose folli). Ho sempre pensato che l' :symbolidea di Ruby fosse inutile; potremmo usare facilmente stringhe semplici invece, come facciamo in JavaScript. E ora decidono di complicare le cose in JS con quello.

Non capisco la motivazione. Qualcuno potrebbe spiegarmi se abbiamo davvero bisogno di simboli in JavaScript?


6
Non so quanto sia autentica questa spiegazione, ma è un inizio: tc39wiki.calculist.org/es6/symbols .
Felix Kling,

8
I simboli consentono così tanto che consentono identificatori univoci con ambito sugli oggetti. Ad esempio, avere proprietà su oggetti accessibili solo in un posto.
Benjamin Gruenbaum,

5
Non ne sono sicuro poiché puoi usare Object.getOwnPropertySymbols (o)
Yanis,

4
È più unicità piuttosto che privacy.
Qantas 94 Pesante,

2
Avrebbero avuto un'implementazione di classe più complicata con privatee publicparole chiave di attributo di classe che hanno deciso di abbandonare per un'implementazione di classe più semplice. Invece di this.x = xte dovevi fare public x = xe per variabili private private y = y. Decisero di abbandonarlo per un'implementazione di classe molto più minimale. Symbol sarebbe quindi una soluzione alternativa per ottenere proprietà private nell'implementazione minima.
lyschoening

Risposte:


224

La motivazione originale per introdurre simboli in Javascript era di abilitare le proprietà private .

Sfortunatamente, hanno finito per essere gravemente declassato. Non sono più privati, poiché puoi trovarli tramite la riflessione, ad esempio, utilizzando Object.getOwnPropertySymbolso proxy.

Ora sono noti come simboli univoci e il loro unico scopo è evitare conflitti di nome tra le proprietà. Ad esempio, lo stesso ECMAScript ora può introdurre hook di estensione tramite determinati metodi che è possibile applicare agli oggetti (ad esempio per definire il loro protocollo di iterazione) senza rischiare che si scontrino con i nomi utente.

È discutibile se sia abbastanza forte una motivazione per aggiungere simboli al linguaggio.


93
La maggior parte delle lingue (tutte tradizionali) forniscono alcuni meccanismi, generalmente riflessivi, per ottenere comunque l'accesso al privato.
Esailija,

19
@Esailija, non penso sia vero - in particolare, poiché molte lingue non offrono in primo luogo la riflessione. Perdere lo stato privato attraverso la riflessione (come ad esempio in Java) dovrebbe essere considerato un bug, non una caratteristica. Ciò è particolarmente vero nelle pagine Web, dove avere uno stato privato affidabile può essere rilevante per la sicurezza. Attualmente, l'unico modo per raggiungerlo in JS è attraverso le chiusure, che possono essere noiose e costose.
Andreas Rossberg,

38
Il meccanismo non deve essere riflesso: C ++, Java, C #, Ruby, Python, PHP, Objective-C consentono tutti l'accesso in un modo o nell'altro se proprio lo si desidera. Non si tratta solo di abilità ma di comunicazione.
Esailija,

4
@plalx, ​​sul web, l'incapsulamento a volte riguarda anche la sicurezza.
Andreas Rossberg,

3
@RolandPihlakas, sfortunatamente, Object.getOwnPropertySymbolsnon è l'unica perdita; la più difficile è la capacità di utilizzare i proxy per intercettare l'accesso a una proprietà "privata".
Andreas Rossberg,

95

I simboli non garantiscono la vera privacy ma possono essere utilizzati per separare le proprietà pubbliche e interne degli oggetti. Facciamo un esempio in cui possiamo usare Symbolper avere proprietà private.

Facciamo un esempio in cui una proprietà di un oggetto non è privata.

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

Sopra, la Petproprietà di classe typenon è privata. Per renderlo privato dobbiamo creare una chiusura. L'esempio seguente mostra come possiamo rendere typeprivato usando una chiusura.

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

Svantaggio dell'approccio sopra: stiamo introducendo una chiusura aggiuntiva per ogni Petistanza creata, che può danneggiare le prestazioni.

Ora presentiamo Symbol. Questo può aiutarci a rendere privata una proprietà senza utilizzare chiusure non necessarie extra. Esempio di codice di seguito:

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
Nota che le proprietà dei simboli non sono private ! I simboli sono senza collisioni . Potresti voler leggere la risposta accettata.
Bergi,

3
Sì, il simbolo non garantisce la vera privacy ma può essere utilizzato per separare le proprietà pubbliche e interne degli oggetti. Siamo spiacenti, ho dimenticato di aggiungere questo punto alla mia risposta. Aggiornerò la mia risposta di conseguenza.
Samar Panda,

@SamarPanda, Potresti anche dire che i membri con prefisso _non garantiscono la vera privacy ma possono essere usati per separare le proprietà pubbliche e interne degli oggetti. In altre parole, risposta inutile.
Pacerier,

10
Non direi inutile, poiché i simboli per impostazione predefinita non sono enumerabili, inoltre non è possibile accedervi per "errore", mentre qualsiasi altra chiave può farlo.
Patrick,

5
Trovo che la tua risposta sia l'unica che in realtà ha un esempio sensato, sul perché vorresti definire l'attributo privato dell'oggetto come un simbolo, anziché solo un normale attributo.
Luis Lobo Borobia,

42

Symbolssono un nuovo tipo speciale di oggetto che può essere utilizzato come nome di proprietà univoco negli oggetti. L'uso di al Symbolposto di string's consente a diversi moduli di creare proprietà che non sono in conflitto tra loro. Symbolspuò anche essere reso privato, in modo che le loro proprietà non siano accessibili a chiunque non abbia già accesso diretto a Symbol.

Symbolssono una nuova primitiva . Proprio come i number, stringe i booleanprimitivi, Symbolhanno una funzione che può essere usata per crearli. A differenza degli altri primitivi, Symbolsnon hanno una sintassi letterale (ad es. Come stringhanno '') - l'unico modo per crearli è con il Symbolcostruttore nel modo seguente:

let symbol = Symbol();

In realtà, Symbolsono solo un modo leggermente diverso di associare proprietà a un oggetto: potresti facilmente fornire i noti Symbolsmetodi standard, proprio come Object.prototype.hasOwnPropertyappare in tutto ciò che eredita Object.

Ecco alcuni dei vantaggi del Symboltipo primitivo.

Symbols avere debuggability incorporato

Symbols può essere data una descrizione, che è in realtà solo utilizzata per il debug per rendere la vita un po 'più semplice quando li si registra su una console.

Symbolspuò essere usato come Objectchiave

Questo è dove Symboldiventa davvero interessante. Sono fortemente intrecciati con oggetti. Symbolpuò essere assegnato come chiave agli oggetti, il che significa che è possibile assegnare un numero illimitato di univoci Symbola un oggetto e avere la garanzia che questi non entreranno mai in conflitto con le stringchiavi o altri unici Symbols.

Symbols può essere utilizzato come valore univoco.

Supponiamo di avere una libreria di registrazione, che comprende più livelli di registro, come logger.levels.DEBUG, logger.levels.INFO, logger.levels.WARNe così via. Nel codice ES5 desideri creare questi strings (così logger.levels.DEBUG === 'debug') o numbers ( logger.levels.DEBUG === 10). Entrambi questi non sono ideali in quanto quei valori non sono valori univoci, ma lo Symbolsono! Quindi logger.levelsdiventa semplicemente:

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

Leggi di più in questo fantastico articolo .


10
Non sono sicuro di capire il tuo esempio, e perché avresti bisogno log.levels = {DEBUG: Symbol('debug')e non semplicemente log.levels = {DEBUG:'debug'}. alla fine è lo stesso. Penso che valga la pena ricordare che i simboli sono invisibili quando si ripetono le chiavi di un oggetto. questa è la loro "cosa"
vsync il

Uno dei vantaggi è che qualcuno non può usare accidentalmente un valore letterale e credere che funzionerebbe per sempre. (Si noti che questo non è un argomento davvero forte, in quanto si può semplicemente usare {}e ottenere lo stesso risultato (come valore univoco), o forse un letterale è preferito in quel progetto, oppure si può dire che è necessario leggere prima il documento.) I personalmente penso che fornisca una buona leggibilità di significato unico nel codice
apple apple

nota quando usato come valore univoco, l'oggetto letterale ha anche la debuggabilità incorporata cioè Symbol("some message")diventa {message:'some message'}, probabilmente l'oggetto qui fa meglio in quanto è possibile aggiungere più campi.
apple apple

38

Questo post parla di Symbol(), fornito con esempi reali che ho potuto trovare / creare e fatti e definizioni che ho trovato.

TLDR;

Il Symbol()è il tipo di dati, introdotto con il rilascio di ECMAScript 6 (ES6).

Ci sono due fatti curiosi sul Simbolo.

  • il primo tipo di dati e il solo tipo di dati in JavaScript che non ha valore letterale

  • qualsiasi variabile, definita con Symbol(), ottiene un contenuto unico, ma non è proprio privata .

  • tutti i dati ha il proprio simbolo, e per gli stessi dati Simboli sarebbe lo stesso . Maggiori informazioni nel paragrafo seguente, altrimenti non è un TLRD; :)

Come posso inizializzare il simbolo?

1. Per ottenere un identificatore univoco con un valore debuggable

Puoi farlo in questo modo:

var mySymbol1 = Symbol();

O in questo modo:

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

La "some text here"stringa non può essere estratta dal simbolo, è solo una descrizione a scopo di debug. Non cambia in alcun modo il comportamento del simbolo. Tuttavia, potresti console.logfarlo (il che è giusto, poiché il valore è per il debug, in modo da non confondere quel registro con un'altra voce di registro):

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

2. Per ottenere un simbolo per alcuni dati di stringa

In questo caso il valore del simbolo viene effettivamente preso in considerazione e in questo modo due simboli potrebbero non essere univoci.

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

Chiamiamo quei simboli simboli di "secondo tipo". Non si intersecano Symbol(data)in alcun modo con i simboli di "primo tipo" (cioè quelli definiti con ).

I successivi due paragrafi riguardano solo il simbolo di primo tipo .

Come posso beneficiare dell'utilizzo di Symbol anziché dei tipi di dati precedenti?

Consideriamo innanzitutto un oggetto, un tipo di dati standard. Potremmo definire alcune coppie chiave-valori lì e avere accesso ai valori specificando la chiave.

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

E se avessimo due persone con il nome di Peter?

Facendo questo:

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

non avrebbe molto senso.

Quindi, sembra essere un problema di due persone assolutamente diverse con lo stesso nome. Facciamo quindi riferimento nuovo Symbol(). È come una persona nella vita reale - ogni persona è unica , ma i loro nomi possono essere uguali. Definiamo due "persone".

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

Ora abbiamo due persone diverse con lo stesso nome. Le nostre persone sono davvero diverse? Loro sono; puoi controllare questo:

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

Come possiamo trarne vantaggio?

Possiamo inserire due voci nel tuo oggetto per le diverse persone e non possono essere confuse in alcun modo.

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

Nota:
vale la pena notare, tuttavia, che stringendo l'oggetto con JSON.stringifyverranno eliminate tutte le coppie inizializzate con un simbolo come chiave.
L'esecuzione Object.keysnon restituirà tali Symbol()->valuecoppie.

Usando questa inizializzazione, è assolutamente impossibile confondere le voci per la prima e la seconda persona. Chiamandoli console.logverranno emessi correttamente i loro secondi nomi.

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

Se utilizzato nell'oggetto, in che modo è diverso rispetto alla definizione di proprietà non enumerabile?

In effetti, esisteva già un modo per definire una proprietà da nascondere Object.keysed enumerare. Ecco qui:

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

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

Che differenza ci Symbol()porta lì? La differenza è che puoi ancora ottenere la proprietà definita con Object.definePropertynel solito modo:

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

E se definito con Symbol come nel paragrafo precedente:

fruit = Symbol("apple");

Avrai la possibilità di ricevere il suo valore solo se conoscendo la sua variabile, cioè

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

Inoltre, la definizione di un'altra proprietà sotto la chiave "apple"farà cadere l'oggetto più vecchio (e se codificato, potrebbe generare un errore). Quindi niente più mele! È un peccato. Facendo riferimento al paragrafo precedente, i simboli sono unici e definiscono una chiave in quanto Symbol()la renderà unica.

Digitare la conversione e il controllo

  • A differenza di altri tipi di dati, è impossibile convertirli Symbol()in qualsiasi altro tipo di dati.

  • È possibile "rendere" un simbolo basato sul tipo di dati primitivo chiamando Symbol(data).

  • In termini di controllo del tipo, non cambia nulla.

    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


È stato migrato dalla documentazione SO?
Knu,

1
@KNU non lo era; Ho raccolto le informazioni e ho scritto questa risposta da solo
nicael,

Risposta davvero bella!
Mihai Alexandru-Ionut,

1
Ottima risposta su Symbol, tuttavia non so ancora perché dovrei usare l'oggetto con i simboli e non un array. Se ho più persone come {"peter": "pan"} {"john": "doe"} mi sembra male metterle in un oggetto. Per lo stesso motivo per cui non faccio lezioni con proprietà duplicate come personFirstName1, personFirstName2. Questo combinato con l'incapacità di stringerlo non vedo benefici solo svantaggi.
eldo

18

Ecco come lo vedo. I simboli forniscono "un ulteriore livello di privacy", impedendo che le chiavi / proprietà di un oggetto vengano esposte attraverso alcuni metodi popolari come Object.keys () e 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…)

Sebbene sia dato un oggetto di per sé, tali proprietà possono ancora essere esposte tramite reflection, proxy, Object.getOwnPropertySymbols () ecc., Non esistono mezzi naturali per accedervi attraverso alcuni metodi diretti, che a volte possono essere sufficienti dal punto di vista OOP.


2

Un simbolo JS è un nuovo tipo di dati primitivo. Sono token che fungono da ID univoci . Un simbolo può essere creato usando il Symbolcostruttore. Prendi ad esempio questo frammento di 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.

È spesso utile utilizzare i simboli come chiavi di proprietà dell'oggetto univoche, ad esempio:

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

I simboli hanno due casi d'uso principali:

  1. Proprietà dell'oggetto "nascosto". Se vogliamo aggiungere una proprietà in un oggetto che "appartiene" a un altro script o libreria, possiamo creare un simbolo e usarlo come chiave di proprietà. Non viene visualizzata una proprietà simbolica for..in, quindi non verrà elaborata accidentalmente insieme ad altre proprietà. Inoltre non sarà possibile accedervi direttamente, perché un altro script non ha il nostro simbolo. Quindi la proprietà sarà protetta dall'uso accidentale o dalla sovrascrittura.

    Quindi possiamo nascondere "di nascosto" qualcosa negli oggetti di cui abbiamo bisogno, ma altri non dovrebbero vedere, usando le proprietà simboliche.

  2. Ci sono molti simboli di sistema usati da JavaScript che sono accessibili come Symbol.*. Possiamo usarli per modificare alcuni comportamenti integrati. Ad esempio, ...... Symbol.iteratorper iterables, Symbol.toPrimitiveper impostare la conversione da oggetto a primitivo e così via.

fonte

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.