2012-05-25 6 views
18

RECAP:Object.prototype è Verboten?

Ok, è stato un po 'da quando ho fatto questa domanda. Come al solito, sono andato a incrementare lo Object.prototype, nonostante tutti gli argomenti validi contro di esso forniti sia qui che altrove sul web. Credo di essere solo quel tipo di coglione testardo.

Ho cercato di trovare un modo risolutivo per impedire al nuovo metodo di mettere a tacere qualsiasi comportamento previsto, che si è rivelata una cosa molto difficile, ma informativa da fare.
Ho imparato molte cose su JavaScript. Non per ultimo non cercherò nulla di così sfacciato come fare scherzi con i prototipi nativi, (ad eccezione di String.prototype.trim per IE < 9).

In questo caso particolare, non utilizzo alcuna librerie, quindi i conflitti non erano la mia preoccupazione principale. Ma avendo scavato un po 'più a fondo nei possibili contrattempi giocando con prototipi nativi, non sono propenso a provare questo codice in combinazione con qualsiasi lib.

Osservando questo approccio prototipo, sono giunto a una migliore comprensione del modello stesso. Stavo trattando i prototipi come una qualche forma di classe astratta tradizionale flessibile, facendomi aggrappare al pensiero tradizionale dell'OOP. Questo punto di vista non rende giustizia al modello prototipo. Douglas Crockford ha scritto su questa trappola, purtroppo lo sfondo rosa mi ha impedito di leggere l'articolo completo.

Ho deciso di aggiornare questa domanda nel caso in cui le persone che leggono questo argomento sono tentate di vedere da sole. Tutto ciò che posso dire è: con tutti i mezzi, fallo. Spero che tu abbia imparato un paio di cose belle, come ho fatto io, prima di decidere di abbandonare questa idea piuttosto stupida. Una funzione semplice potrebbe funzionare altrettanto bene, o anche meglio, specialmente in questo caso. Dopo tutto, la sua vera bellezza è che, aggiungendo solo 3 linee di codice, è possibile utilizzare la stessa funzione per aumentare ugualmente prototipi di oggetti specifici.


so che sto per fare una domanda che è stato intorno per un bel po ', ma: Perché Object.prototype è considerato off limits ? È lì, e può essere aumentato allo stesso modo, come qualsiasi altro prototipo. Perché, quindi, non dovresti approfittarne. A mio parere, finché sai cosa stai facendo, non c'è motivo di stare alla larga dal prototipo Object.
Prendete questo metodo, ad esempio:

if (!Object.prototype.getProperties) 
{ 
    Object.prototype.getProperties = function(f) 
    { 
     "use strict"; 
     var i,ret; 
     f = f || false; 
     ret = []; 
     for (i in this) 
     { 
      if (this.hasOwnProperty(i)) 
      { 
       if (f === false && typeof this[i] === 'function') 
       { 
        continue; 
       } 
       ret.push(i); 
      } 
     } 
     return ret; 
    }; 
} 

In sostanza, è la stessa vecchia for...in ciclo si sia tenere al sicuro in una funzione, o scrivere più e più volte. So che verrà aggiunto a tutti gli oggetti e poiché quasi tutte le catene di ereditarietà in JavaScript possono essere ricondotte allo Object.prototype, ma nel mio script, lo considero il minore di due mali.

Forse qualcuno potrebbe fare un lavoro migliore a dirmi dove ho sbagliato rispetto a this chap, tra gli altri.
Mentre cercavamo i motivi, NON per toccare il prototipo dello Object, una cosa continuava a spuntare: rompeva lo for..in loop-thingy, ma poi di nuovo: anche molte strutture, per non parlare delle vostre catene ereditarie. È quindi una cattiva pratica non includere un controllo .hasOwnProperty durante il looping delle proprietà di un oggetto, a mio avviso.

Ho trovato anche this piuttosto interessante.Ancora: un commento è abbastanza inequivocabile: estendere i prototipi nativi è una cattiva pratica, ma se i V8 lo fanno, chi sono io per dire che si sbagliano?
Lo so, questo argomento non si accumula abbastanza.

Il punto è: non riesco a vedere un problema con il codice precedente. Mi piace, lo uso molto e finora non mi ha deluso una volta. Sto anche pensando di collegare un paio di funzioni al prototipo Object. A meno che qualcuno non possa dirmi perché non dovrei, è così.

+0

Non ci ho mai pensato. Ma quali strutture infrangono il ciclo 'for ... in'? E per favore spiega come la mia "catena ereditaria" rompe "per ... in"? Non sto cercando di essere testardo, semplicemente non capisco come quegli esempi rompano il ciclo 'for ... in'. Forse un esempio? entrambi gli altri framework e un esempio di interruzione ereditaria personalizzata 'for ... in'. –

+2

chi ha svalutato la mia domanda: per favore, spiega perché –

+0

ama la parte aggiornata: D – c69

risposta

13

Il fatto è che va bene finchè sai cosa stai facendo e quali sono i costi. Ma è un grande "se". Alcuni esempi dei costi:

  • Avrai bisogno di fare vasta test con qualsiasi biblioteca si sceglie di utilizzare, con un ambiente che aumenta Object.prototype, perché la convenzione schiacciante è che un oggetto vuoto avrà nessuna proprietà enumerabile. Aggiungendo una proprietà enumerabile a Object.prototype, stai rendendo falsa quella convenzione. Per esempio, questo è abbastanza comune:

    var obj = {"a": 1, "b": 2}; 
    var name; 
    for (name in obj) { 
        console.log(name); 
    } 
    

    ... con la schiacciante convenzione essere che solo "a" e "b" verrà visualizzato, non "getProperties".

  • Chiunque lavori al codice dovrà essere istruito sul fatto che tale convenzione (sopra) non viene seguita.

è possibile attenuare quanto sopra utilizzando Object.defineProperty (e simili), se supportata, ma attenzione che anche nel 2014, browsers come IE8 che don't support it rimangono correttamente in uso significativo (anche se possiamo sperare che cambierà rapidamente ora che XP è ufficialmente EOL'd). Questo perché utilizzando Object.defineProperty, è possibile aggiungere non enumerabili proprietà (quelli che non mostrano in for-in loop) e così avrete molti meno problemi (a quel punto, si è soprattutto preoccupato di conflitti di nome)   — ma funziona solo su sistemi che implementano correttamente Object.defineProperty (e un'implementazione corretta non può essere "shimmed").

Nel tuo esempio, non aggiungereia Object.prototype; Lo aggiungerei allo Object e accetto l'oggetto come argomento, come fa ES5 per getPrototypeOf e simili.

Tenere presente che la libreria Prototype ottiene un sacco di problemi per l'estensione di Array.prototype a causa di come questo influisce sui cicli for..in. E questo è solo Array s (che non si dovrebbe usare for..in su ogni caso (a meno che non si sta utilizzando la guardia hasOwnProperty e molto probabilmente String(Number(name)) === name pure).

... se la gente V8 lo fanno, chi sono io per dire che si sbagliano?

sul V8, si può contare su Object.defineProperty, perché V8 è un motore del tutto ES5-compliant.

Nota che anche quando le proprietà sono non numerabile, ci sono anni fa, Prototype (indirettamente) ha definito unoFunzionesu Array.prototype.E fa quello che ti aspetti: chiama una funzione iteratore e crea un nuovo array basato sugli elementi scelti dalla funzione. Quindi ECMAScript5 è arrivato e ha definito Array.prototype.filter di fare la stessa cosa. Ma c'è il problema: Molto la stessa cosa. In particolare, la firma delle funzioni iteratore che vengono chiamate è diversa (ECMAScript5 include un argomento che Prototype non ha). Potrebbe essere stato molto peggio (e ho il sospetto che sia   — ma non possa provare   — che TC39 fosse a conoscenza del prototipo e abbia intenzionalmente evitato troppi conflitti con esso).

Quindi: se hai intenzione di farlo, sii consapevole dei rischi e dei costi. I brutti bug di bordo che si possono incontrare come risultato del tentativo di utilizzare librerie off-the-shelf potrebbero davvero farti perdere tempo ...

+0

Ciao, e grazie per lo sforzo. Una risposta molto informativa davvero. Proprio come un riassunto: aggiungendo 'enumerable: false' al mio metodo e aggiungendo il suggerimento di HPB (per i browser del motore V8), sono ragionevolmente al sicuro, no? Vorrei aggiungere che non sto usando alcuna lib (eliminata da jQuery), quindi non ci sono problemi, immagino ...Approfondirò questo domani, ora è ora di passare la notte-notte :) –

+0

@EliasVanOotegem: Dipende da cosa intendi per "ragionevolmente sicuro". Vedere la cosa del prototipo alla fine, dove anche non essere enumerabile non sarebbe d'aiuto perché i nomi sono in conflitto. Se lo farai, userei una sorta di prefisso per rendere molto improbabile che entrerai in conflitto con qualcosa che verrà aggiunto in seguito. –

+0

Sì, pensavo anche a me. grazie ancora –

2

Se framework e librerie generalmente facevano quello che stai proponendo, sarebbe molto Presto capiterà che due diversi framework definiranno due diverse funzionalità come lo stesso metodo di Object (o Array, Number ... o uno qualsiasi dei prototipi di oggetti esistenti). È quindi preferibile aggiungere tale nuova funzionalità nel proprio spazio dei nomi.

Per esempio ... immaginare, si avrebbe una libreria che serializzare oggetti JSON e una biblioteca che li avrebbe serializzare in XML ed entrambi avrebbero definire la loro funzionalità

Object.prototype.serialize = function() { ... } 

e si sarebbe solo in grado di utilizzare quello che è stato definito in seguito. Quindi è meglio se non lo fanno, ma invece

JSONSerializingLibrary.seralize = function(obj) { ... } 
XMLSerializingLibrary.seralize = function(obj) { ... } 

Si potrebbe anche accadere che una nuova funzionalità è definito in un nuovo standard ECMAScript, o aggiunti da un fornitore del browser. Quindi immagina che i tuoi browser aggiungano anche una funzione serialize. Ciò causerebbe nuovamente conflitti con le librerie che hanno definito la stessa funzione. Anche se la funzionalità delle librerie fosse uguale a quella incorporata nel browser, le funzioni di script interpretate annullerebbero la funzione nativa che, in realtà, sarebbe più veloce.

+0

Grazie per lo sforzo, vedo che ho omesso di menzionare che non sto usando alcun framework/lib di alcun tipo, quindi non c'è conflitto lì. Quando si tratta di nuovi standard ES: non ne sono affatto preoccupato, dal momento che mi sono già occupato di un problema simile e non si è rivelato così difficile da aggirare. –

1

Vedi http://www.websanova.com/tutorials/javascript/extending-javascript-the-right-way

che affronta alcuni, ma non tutti, le obiezioni sollevate. L'obiezione relativa alle diverse librerie che creano metodi di contrasto può essere alleviata sollevando un'eccezione se un metodo specifico del dominio è già presente in Object.prototype. Ciò fornirà almeno un avviso quando si verificherà questo evento indesiderato.

Ispirato da questo post ho sviluppato il seguente che è anche disponibile nei commenti della pagina citata.

!Object.implement && Object.defineProperty (Object.prototype, 'implement', { 
    // based on http://www.websanova.com/tutorials/javascript/extending-javascript-the-right-way 
    value: function (mthd, fnc, cfg) { // adds fnc to prototype under name mthd 
     if (typeof mthd === 'function') { // find mthd from function source 
     cfg = fnc, fnc = mthd; 
     (mthd = (fnc.toString().match (/^function\s+([a-z$_][\w$]+)/i) || [0, ''])[1]); 
     } 
     mthd && !this.prototype[mthd] && 
     Object.defineProperty (this.prototype, mthd, {configurable: !!cfg, value: fnc, enumerable: false}); 
    } 
}); 

Object.implement (function forEach (fnc) { 
    for (var key in this) 
    this.hasOwnProperty (key) && fnc (this[key], key, this); 
}); 

L'ho usato principalmente per aggiungere funzioni standard definite sull'implementazione che non le supportano.

+0

Ok, non ho provato completamente questo suggerimento, ma cercherò di approfondirlo ulteriormente dopo un po 'di kip. Grazie per il link btw. Non sono del tutto sicuro che il tuo suggerimento possa funzionare su IE8, il che, sono triste a dirsi, deve ancora arrivare a quel punto nel tempo in cui non sarà nient'altro che un brutto sogno. –

+0

Sarei interessato ai risultati dei test. – HBP

+0

Ok, sono tornato su una finestra di Windows con IE8 e ho controllato il tuo suggerimento. Sfortunatamente JScript di IE8 non è JavaScript e non supporta il metodo defineProperty. Una rapida ricerca su google mi dice che non c'è niente da fare, ma usare un blocco try-catch per supportare sia i browser conformi allo standard ECMA (noti anche come buoni) sia la scusa inutile di un browser. Credo che ora so perché l'acronimo MS mi ricorda una malattia degenerativa, debilitante e terribile. Tuttavia, ora riesco a capire perché Object.prototype genera problemi durante lo sviluppo di browser meno recenti. Grazie ancora per i link e lo sforzo –

Problemi correlati