2012-05-07 18 views
15

Mozilla ha affermato che rimuovere __proto__ un istante indietro (~ 2008) ed è ancora nel browser. Sarà ancora deprecato? Funziona in Opera, (Safari credo) e anche in Chrome. Non ho bisogno di preoccuparmi di IE, quindi mi piacerebbe continuare a usarlo.__proto__, quando sarà sparito? Alternative?

Tuttavia, non voglio il mio codice di smettere di lavorare un giorno, così alla mia domanda:

__proto__ permette morto semplice eredità:

a.__proto__ = {'a':'test'} 

C'è qualche cosa che posso replicare questo in un modo conforme agli standard? So che esiste un'eredità funzionale, che è brutta, e complica il fatto che voglio solo creare una catena di prototipi. Mi chiedo solo se qualche stregone ha risolto questo.

Grazie

+3

Sembra che "__proto__" possa essere standardizzato nel prossimo ECMAScript. Non posso essere assolutamente sicuro fino a quando non sarà finalizzato. http://wiki.ecmascript.org/doku.php?id=harmony:specification_drafts ... almeno è referenziato nella bozza attuale nell'Allegato B, Funzioni aggiuntive per i browser Web. –

risposta

22

Nota: È considerata una cattiva prassi modificare il valore di __proto__. Farlo è fortemente scoraggiato da Brendan Eich, il creatore di JavaScript, tra gli altri. In effetti la proprietà __proto__ è stata completamente rimossa da alcuni motori JavaScript come Rhino. Se vuoi sapere perché allora leggi lo following comment di Brendan Eich.

Aggiornamento: I browser non rimuovono la proprietà __proto__. Infatti, ECMAScript Harmony ha ora standardizzato sia la proprietà __proto__ sia la funzione setPrototypeOf. La proprietà __proto__ è supportata solo per motivi legacy. Si consiglia vivamente di utilizzare setPrototypeOf e getPrototypeOf anziché __proto__.

Attenzione: Anche se setPrototypeOf è ora uno standard, si sta ancora scoraggiati dal usarlo perché mutando il prototipo di un oggetto uccide invariabilmente ottimizzazioni e rende il codice più lento. Inoltre, l'uso di setPrototypeOf è solitamente un'indicazione di codice di scarsa qualità.


Non è necessario preoccuparsi che il codice esistente non funzioni un giorno. La proprietà __proto__ è qui per rimanere.

Ora, per la domanda in questione. Vogliamo fare qualcosa di simile a questo in un modo compatibile con gli standard:

var a = { 
    b: "ok" 
}; 

a.__proto__ = { 
    a: "test" 
}; 

alert(a.a); // alerts test 
alert(a.b); // alerts ok 

Ovviamente non è possibile utilizzare Object.create per raggiungere questo fine dal momento che non si sta creando un nuovo oggetto. Stiamo solo provando a modificare la proprietà interna [[proto]] dell'oggetto specificato. Il problema è che non è possibile modificare la proprietà interna di [[proto]] di un oggetto una volta creata (eccetto usando __proto__ che stiamo cercando di evitare).

Quindi, per risolvere questo problema ho scritto una semplice funzione (si noti che funziona per tutti gli oggetti ad eccezione di funzioni):

function setPrototypeOf(obj, proto) { 
    var result = Object.create(proto); 
    var names = Object.getOwnPropertyNames(obj); 
    var getProp = Object.getOwnPropertyDescriptor; 
    var setProp = Object.defineProperty; 
    var length = names.length; 
    var index = 0; 

    while (index < length) { 
     var name = names[index++]; 
     setProp(result, name, getProp(obj, name)); 
    } 

    return result; 
} 

Così possiamo ora modificare il prototipo di un oggetto dopo che è creato come segue (presente che non sono effettivamente cambiando il [[proto]] struttura interna dell'oggetto ma invece creare un nuovo oggetto con le stesse proprietà come oggetto dato e che eredita dalla proposta prototipo):

var a = { 
 
    b: "ok" 
 
}; 
 

 
a = setPrototypeOf(a, { 
 
    a: "test" 
 
}); 
 

 
alert(a.a); // alerts test 
 
alert(a.b); // alerts ok
<script> 
 
function setPrototypeOf(obj, proto) { 
 
    var result = Object.create(proto); 
 
    var names = Object.getOwnPropertyNames(obj); 
 
    var getProp = Object.getOwnPropertyDescriptor; 
 
    var setProp = Object.defineProperty; 
 
    var length = names.length; 
 
    var index = 0; 
 

 
    while (index < length) { 
 
     var name = names[index++]; 
 
     setProp(result, name, getProp(obj, name)); 
 
    } 
 

 
    return result; 
 
} 
 
</script>

Semplice ed efficiente (e non abbiamo utilizzato la proprietà __proto__).

+0

Ottima risposta. Grazie! – jdw

+2

Stai dicendo letteralmente alle persone di scrivere codice rotto, contro il consiglio esplicito sia degli enti standard che degli implementatori del motore JS. A seconda di bug come questo è come le API e gli interni di Windows devono essere un tale casino. Se puoi evitarlo, davvero non dovresti. E davvero non penso che dovresti raccomandarlo senza nessun avvertimento. Né tu né io siamo più autorevoli delle specifiche o degli implementatori della lingua. Ricorda: non esiste in realtà una versione di JavaScript che includa '__proto__' - è un dettaglio interno che alcune implementazioni possono rivelare. – Chuck

+7

@Chuck, la proprietà '__proto__' viene considerata come uno standard nella prossima versione di JavaScript come @cliffsofinsanity di cui sopra. Gli implementatori sconsigliano di usarlo perché non è ancora uno standard. Quindi potrebbe non funzionare in tutti i motori JavaScript. È sempre una buona idea capire perché non è consigliabile utilizzare una determinata funzione invece di criticare solo le persone. Usare la proprietà '__proto__' sul web è perfettamente a posto dato che tutti i principali browser lo supportano. Non vedo alcun avvertimento di usarlo, salvo che non funzionerà con Rhino. Non ha nulla a che fare con Windows. –

1

Io non vedo come:

var a = {}; 
a.__proto__ = A; // A is object because no constructor is needed 

è più semplice:

var a = new A(); // A is Constructor function 

Ora la creazione di un in quest'ultimo caso può essere più dettagliato se si don' Ho bisogno di una funzione di costruzione, ma in questo caso è possibile automatizzarlo con :

var A = Constructor({ 
    method: function(){} 
}); // A is function, meant to be used with new 

Rispetto a:

var A = { 
    method: function(){} 
}; //A is object, meant to be set as __proto__ 

E se avete bisogno di qualche logica di inizializzazione, sarà probabilmente finiscono per essere più facile solo per usare modo normale in cui la funzione di costruzione deve essere dichiarato in ogni caso

+0

Per la logica init, è possibile utilizzare una factory anziché fare affidamento su un costruttore. Hai bisogno di scavare un po 'più a fondo per vedere il valore. Se le tue due versioni avevano prototipi, Object.create è un po 'più semplice. Vedi: function Constructor() {}; Constructor.prototype = {/ * metodi * /}; var newObject = new Constructor(); ** VS ** var proto = {/ * metodi * /}; var newObject = Object.create (proto) ;. Ho appena creato un nuovo oggetto prototipo in meno codice rispetto alla definizione del mio ereditario classico. – Drew

+1

@Drew Prima di tutto, la prima non usa l'ereditarietà classica. In secondo luogo, è solo il costo di installazione una tantum che richiede più codice, la creazione effettiva di oggetti non è solo un codice inferiore, ma consente la logica init senza creare una factory separata. Ricorda che il costruttore in JS funziona come qualsiasi altra funzione, non è limitato come i costruttori in linguaggio classico, quindi molti motivi per creare una fabbrica in quelle lingue non si applicano a js. – Esailija

7

Penso che il punto attuale che Mozilla voleva fare fosse che non è standard, quindi gli implementatori sarebbero perfettamente nei loro diritti rimuovendoli.

Il modo più semplice per creare catene prototipo è Object.create. L'equivalente del codice, per creare un oggetto a con il prototipo {'a': 'test'}, è:

a = Object.create({'a':'test'}) 

ci sono spessori anche di imitare questa funzione nei browser che non lo supportano, se mai hai bisogno di lavorare con uno , che è un altro vantaggio rispetto allo scherzo diretto con __proto__.

+0

Non penso che l'OP voglia creare un nuovo oggetto con un determinato prototipo. Penso che voglia cambiare il prototipo di un oggetto esistente senza modificare '__proto__'. –

+1

@AaditMShah: la sua descrizione era che "desidera solo [s] creare una catena di prototipi" in contrasto con "l'ereditarietà funzionale". Ciò suggerisce che vuole l'ereditarietà del prototipo puro, che è ciò che Object.create è per. I prototipi che ruotano dinamicamente sono utili, ma non così comunemente necessari. – Chuck

+0

Inoltre, la mutazione dell'oggetto mediante la riassegnazione del prototipo è molto più lenta e scoraggiata. Object.create è molto più veloce. –