2009-12-28 12 views
7

Blick durch die dom.js Quelle aus der Schließung Bibliothek ich diese gefunden (in goog.dom.getElementsByTagNameAndClass_):Array-ähnliches Objekt 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; 
} 

Was wäre der Vorteil dieses über eine regelmäßige Anordnung zu tun?

Antwort

3

Der Autor des Codes verwendete leeres JavaScript-Objekt als eine Basis eines arrayähnlichen Objekts, d. H. Dasjenige, auf das durch den Index zugegriffen werden kann und das eine Längeneigenschaft hat.

Es könnte zwei Gründe, die ich denken konnte:

  1. Speicher verwenden - wenn Array durch Umsetzung gesichert ist, die n Elemente zuordnet, wenn es seine Grenzen es um einen bestimmten Faktor wächst erreicht Kapazität, es ist zu erhöhen, und verschwendet so capacity - length des Speichers
  2. CPU-Zeit - Implementierer wählen Einfügungsgeschwindigkeit über Random-Access-Geschwindigkeit - eine Rückkehr von dieser Methode wird eher über die sequentielle Iteration als Random zugegriffen, und Größenanpassung der Array in Einfügung verursacht Zuordnung-Kopie- Deallokation, die eine CPU-Strafe hat

Ich wette, dass ähnliche Code in anderen JavaScript-Bibliotheken gefunden werden würde, und dass es Ergebnis des Benchmarking ist und die beste Lösung für die verschiedenen Browser zu finden.

nach Kommentar von Justin

weiter bearbeitet Nach googeln es, dass die Array-ähnliche Objekte sind häufig bei JavaScript-Entwickler erscheint: Kasse JavaScript: The Definitive Guide von David Flanagan, hat es eine ganze sub-chapter on Array-like objects. Auch theseguys erwähnen Sie sie.

Keine Erwähnung von warum sollte Array-artig gegenüber Array-Objekt bevorzugen. Dies könnte eine gute Frage sein.

Eine dritte Option könnte also der Schlüssel sein: die Einhaltung der Normen von JavaScript-APIs.

+0

Die Kapazität wächst nicht notwendigerweise mit jedem Einsatz. Es ist eine allgemeine Optimierung, die Kapazität um einen Wert größer als 1 zu erhöhen, wenn die Kapazität eines Containers erreicht wird. Ich habe nicht den Code des tatsächlichen Motors angeschaut, aber ich wäre bereit, Geld dafür zu geben, dass diese Optimierung vorhanden ist. Was CPU-Zeit angeht, zeigt meine Benchmark das Gegenteil, vorausgesetzt, dass es nur in FF 3.5.6 ist. –

+0

Sie haben natürlich Recht, aber ich wollte nicht, dass die Kapazität mit jeder Insertion steigt ... –

-3

Soweit ich weiß, gibt es keinen Vorteil, da es tatsächlich keinen Unterschied zwischen diesem und einem "regulären Array" abgesehen von der Erstellungssyntax gibt - alle Javascript-Objekte sind assoziative Arrays.

+0

Nicht genau. Objekte/assoziative Arrays haben keine 'length'-Eigenschaft, weshalb die Closure-Bibliothek sie in diesem Fall hinzufügt (zB:' console.log ({}. Length, [] .length) '=>" undefined 0 "). –

+0

Auch Arrays ([]) haben einen breiteren Satz von Methoden (siehe Array.prototype). – Nickolay

+0

Arrays erweitern Objekte (assoziative Arrays); Es fügt alle Array-Methoden hinzu und verfolgt die Länge. Beachten Sie, dass 'length 'keine nicht ganzzahligen Objekte verfolgt. Es ist PHP, dass sie genau das gleiche sind –

2

In diesem Fall würde ich vermuten, dass arrayLike[len++] = el eine Optimierung über actualArray.push(el) ist. Nach einem einfachen Benchmark (der Code ist unten aufgeführt) scheint es jedoch so zu sein, dass diese Methode langsamer ist als die Verwendung eines Standard-Arrays unter Verwendung der push-Methode sowie mit derselben Konstruktionstechnik.

Ergebnisse (ab OS X 10.5.8, FF 3.5.6) *:

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

Abschließend warum Closure ein assoziatives Array ist in diesem Fall verwendet, ist mir schleierhaft. Möglicherweise gibt es einen Grund (zum Beispiel, dass diese Technik in Chrome besser abschneidet, oder weniger fragwürdig, diese Technik kann in zukünftigen Versionen von JavaScript-Engines im Allgemeinen besser funktionieren), aber ich sehe in diesem Fall keinen guten Grund.

* Ein Mittelwert wurde nicht angegeben, da die Zeiten von Testlauf zu Testlauf variierten, aber in der gleichen Reihenfolge übereinstimmten. Wenn Sie interessiert sind, können Sie es selbst durchführen.

Benchmark-Code:

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"); 

Die Größe und Art der value ist unbedeutend auf die Probe als JavaScript copy-on-write verwendet. Ein großer (1kb) value wurde verwendet, um diejenigen Leser zu überzeugen, die nicht mit dieser Eigenschaft von JavaScript vertraut sind.

+2

Immer wenn Sie einen Benchmark machen, denken Sie daran, gegen IE 6 zu messen. Viele seltsame Performance-Hacks, die Sie sehen, können da sein, um einige schreckliche Leistungsmerkmale von IE 6 zu umgehen, während sie wenig oder keinen Nutzen bieten andere Browser. –

+0

In der Tat bin ich mir dessen bewusst. Aber, wie ich schon sagte, ich bin gerade auf einem Mac und kann nicht auf einer ganzen Reihe von Browsern testen. –

+0

Genau, Brian. Und es würde Sinn machen, IE wegen des Marktanteils des Browsers für die Leistung zu bevorzugen (es gibt eine Menge von Hassern da draußen, aber Fakten sind Fakten). Stellen Sie außerdem sicher, dass Sie diesen Test mit Googles V8-Engine durchführen. –

0

Ich sehe keinen Unterschied, da alert(typeof []); "object" zurückgibt. Jedes Mal, wenn ich so etwas sehe (besonders aus dem Goog), muss ich davon ausgehen, dass es für einen Leistungsschub ist.

Im Wesentlichen wird ein Array ohne alle geerbten Funktionen wie push und pop zurückgegeben.

+0

Ich dachte das gleiche auch. Es stellte sich heraus, dass wir beide falsch lagen. Sehen Sie sich meinen Benchmark an. –

+0

Tatsächlich war Google Closure beim letzten Mal überhaupt nicht für die Leistung optimiert und sah eher wie ein Legacy-Code aus. – kangax

0
goog.dom.getElementsByTagNameAndClass_ 

Sie haben es mit HTML-Sammlungen zu tun. Ein arrayLike-Objekt ist ein Snapshot einer Knotenliste, und nicht das "Live" -Auflistungsobjekt. Es ist so einfach wie ein indiziertes Array, und es ist weniger wahrscheinlich, dass es zu Komplikationen führt, wenn Sie Knoten erstellen oder löschen, während Sie durch die Mitglieder blättern.

2

Ich denke, dieses Beispiel erstellt ein array-ähnliches Objekt anstelle eines realen Arrays, weil andere DOM-Methoden auch Array-ähnliche Objekte (NodeList) zurückgeben.

Konsequent „Array-likes“ in der API zwingt die Entwickler unter Verwendung der Array-spezifischen Methoden (mit goog.array statt) zu vermeiden, so gibt es weniger gotchas, wenn jemand später einen getElementsByTagNameAndClass Anruf zu ändern entscheidet, sagen wir, getElementByTagName .