2009-12-28 18 views
7

Guardando attraverso la fonte dom.js dalla libreria Chiusura ho trovato questo (in goog.dom.getElementsByTagNameAndClass_):oggetto Array-come in javascript

if (opt_class) { 
var arrayLike = {}; 
var len = 0; 
for (var i = 0, el; el = els[i]; i++) { 
    var className = el.className; 
    // Check if className has a split function since SVG className does not. 
    if (typeof className.split == 'function' && 
     goog.array.contains(className.split(' '), opt_class)) { 
    arrayLike[len++] = el; 
    } 
} 
arrayLike.length = len; 
return arrayLike; 
} 

Quale sarebbe il vantaggio di fare questo oltre una serie regolare?

risposta

3

L'autore del codice ha utilizzato l'oggetto JavaScript vuoto come base di un oggetto come l'array, ovvero quello a cui è possibile accedere tramite indice e con una proprietà length.

ci possono essere due motivi che ho potuto pensare di:

  1. memoria uso - se matrice è sostenuta da implementazione che alloca n elementi, quando raggiunge suoi limiti cresce da qualche fattore per aumentare la sua capacità, e quindi spreca capacity - length di memoria
  2. tempo di cpu - l'implementatore sceglie la velocità di inserimento in base alla velocità di accesso casuale - un ritorno da questo metodo è più probabile che venga iterato su sequenziale dell'accesso casuale e il ridimensionamento dell'array nell'inserimento causa allocazione-copia- deallocazione che ha una penalità di cpu

Scommetto che un codice simile potrebbe essere trovato in altre librerie JavaScript e che è il risultato di benchmarking e di trovare la soluzione migliore per adattarsi a diversi browser.

modificato dopo commento di Justin

Su ulteriore googling sembra che gli oggetti array come sono comuni tra gli sviluppatori JavaScript: checkout JavaScript: The Definitive Guide di David Flanagan, ha un intero sub-chapter on Array-like objects. Anche theseguys menzionarli.

Nessuna menzione del motivo per cui si dovrebbe preferire un oggetto matrice come array. Questa potrebbe essere una buona domanda.

Quindi una terza opzione potrebbe essere la chiave: la conformità con le norme delle API JavaScript.

+0

La capacità non cresce necessariamente con ogni inserto. È un'ottimizzazione comune aumentare la capacità di un valore superiore a 1 ogni volta che viene raggiunta la capacità di un contenitore. Non ho guardato il vero codice del motore, ma sarei disposto a mettere soldi su quell'ottimizzazione presente. Per quanto riguarda il tempo della CPU, il mio benchmark mostra il contrario, ammesso che sia solo in FF 3.5.6. –

+0

Ovviamente, sì, ma non volevo (intendevo) implicare che la capacità aumentasse ad ogni inserimento ... –

-3

Per quanto ne so, non vi è alcun vantaggio perché non c'è in realtà alcuna differenza tra questo e un "array normale" ad eccezione della sintassi di creazione - tutti gli oggetti Javascript sono array associativi.

+0

Non esattamente. Gli oggetti/array associativi non hanno una proprietà 'length', motivo per cui la libreria Closure la sta aggiungendo in questo caso (es .:' console.log ({}. Length, [] .length) '=>" undefined 0 "). –

+0

Inoltre, gli array ([]) dispongono di un insieme più ampio di metodi (vedere Array.prototype). – Nickolay

+0

Le matrici estendono gli oggetti (matrici associative); aggiunge tutti i metodi dell'array, inoltre tiene traccia della lunghezza. Si noti che 'length' non tiene traccia degli oggetti con chiave non intera. È PHP che sono esattamente gli stessi –

2

In questo caso, la mia ipotesi sarebbe che arrayLike[len++] = el è un'ottimizzazione su actualArray.push(el). Tuttavia, dopo aver eseguito un semplice benchmark (codice fornito sotto i risultati), sembra che questo metodo sia effettivamente più lento rispetto all'utilizzo di un array standard utilizzando il metodo push e con la stessa tecnica di costruzione.

Risultati (da OS X 10.5.8, FF 3.5.6) *:

push construction:  199ms (fastest) 
indexed construction:  209ms 
associative construction: 258ms (slowest) 

In conclusione, il motivo per cui la chiusura utilizza un array associativo in questo caso è di là di me. Potrebbe esserci un motivo (ad esempio, questa tecnica potrebbe funzionare meglio in Chrome, o meno dubbiosamente, questa tecnica potrebbe funzionare meglio nelle versioni future dei motori JavaScript in generale), ma in questo caso non vedo una buona ragione.

* Non è stata fornita una media perché i tempi variavano dall'esecuzione del test alla corsa di prova, ma consisteva nello stesso ordine. Se sei interessato, puoi portarlo avanti da solo.

Codice di riferimento:

var MAX = 100000, i = 0, 
    a1 = {}, a2 = [], a3 = [], 
    value = ""; 

for (i=0; i<1024; ++i) { 
    value += "a"; 
} 

console.time("associative construction"); 
for (i=0; i<MAX; ++i) { 
    a1[i] = value; 
} 
a1.length = i; 
console.timeEnd("associative construction"); 

console.time("push construction"); 
for (i=0; i<MAX; ++i) { 
    a2.push(value); 
} 
console.timeEnd("push construction"); 

console.time("indexed construction"); 
for (i=0; i<MAX; ++i) { 
    a3[i] = value; 
} 
console.timeEnd("indexed construction"); 

Le dimensioni e il tipo di value è insignificante alla prova come utilizza JavaScript copy-on-write. Un grande (1kb) value è stato utilizzato allo scopo di convincere i lettori che non hanno familiarità con questa funzionalità di JavaScript.

+2

Ogni volta che si fa un benchmark, ricordarsi di fare un benchmark con IE 6. Un sacco di strani hacks delle prestazioni che si vedono potrebbero essere lì per aggirare alcune orribili caratteristiche delle prestazioni di IE 6, fornendo poco o nessun vantaggio su altri browser. –

+0

In effetti, ne sono consapevole. Ma, come ho detto, al momento sono su un Mac e non riesco a testare su una gamma completa di browser. –

+0

Esattamente, Brian. E sarebbe logico favorire IE sulle prestazioni a causa della quota di mercato dei browser (ci sono molti odiatori là fuori, ma i fatti sono fatti). Inoltre, assicurati di eseguire questo test con il motore V8 di Google. –

0

Non vedo alcuna differenza poiché alert(typeof []); restituisce "oggetto". Ogni volta che vedo qualcosa di simile (specialmente dal Goog), devo presumere che sia per una spinta in termini di prestazioni.

È essenzialmente la reticolazione di un array senza tutte le funzioni ereditate come push e pop.

+0

Anche io pensavo lo stesso. Risulta che entrambi abbiamo sbagliato. Vedi il mio benchmark –

+0

In realtà, l'ultima volta che ho controllato, Google Closure non era ottimizzato per le prestazioni e sembrava più un codice legacy. – kangax

0
goog.dom.getElementsByTagNameAndClass_ 

Si tratta di raccolte html. Un oggetto arrayLike è uno snapshot di un elenco di nodi, anziché l'oggetto di raccolta "live". Rende facile come un array indicizzato con cui lavorare e meno probabilità di causare complicazioni se si creano o si eliminano nodi mentre si esegue il looping tra i suoi membri.

2

Penso che questo esempio crei un oggetto simile ad un array invece di un array reale, perché anche altri metodi DOM restituiscono oggetti tipo array (NodeList).

Coerentemente con "array-calibro" nelle API costringe lo sviluppatore di evitare di usare i metodi specifici di array (utilizzando goog.array invece), quindi ci sono meno grattacapi quando qualcuno decide in seguito di cambiare una chiamata getElementsByTagNameAndClass a, diciamo, getElementByTagName .