2009-12-28 18 views
7

Mirando a través de la fuente de la biblioteca dom.js Cierre encontré esto (en goog.dom.getElementsByTagNameAndClass_):objeto Array-como en 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; 
} 

¿Cuál sería el beneficio de hacer esto a través de una matriz regular?

Respuesta

3

El autor del código utiliza un objeto JavaScript vacío como base de un objeto parecido a una matriz, es decir, al que se puede acceder por índice y tiene una propiedad de longitud.

Podría haber dos razones por las que se me ocurrieron:

  1. uso de memoria - si el array está respaldado por la aplicación que asigna n elementos, cuando llega a sus límites crece por algún factor para aumentar su capacidad, y así desperdicia capacity - length de memoria
  2. tiempo de CPU - implementador elige la velocidad de inserción sobre la velocidad de acceso aleatorio - un retorno de este método es más probable iterar secuencialmente que acceso aleatorio, y cambiar el tamaño de la matriz en la inserción causa asignación-copia- desasignación que tiene una penalización de cpu

Estoy apostando a que se encontraría un código similar en otras bibliotecas de JavaScript, y que es el resultado de la evaluación comparativa y la búsqueda de la mejor solución para todos los navegadores.

editado tras las observaciones de Justin

Al googlear más parece que los objetos de matriz-como son comunes entre los desarrolladores de JavaScript: Comprobar Javascript: la guía definitiva de David Flanagan, tiene toda una sub-chapter on Array-like objects. También theseguys menciónelos.

No hay mención de por qué debería uno preferir el objeto array-like vs array. Esta podría ser una buena pregunta SO.

Así que una tercera opción podría ser la clave: el cumplimiento de las normas de la API de JavaScript.

+0

La capacidad no necesariamente crece con cada inserción. Es una optimización común para aumentar la capacidad en un valor mayor que 1 siempre que se alcance la capacidad de un contenedor. No he visto el código del motor real, pero estaría dispuesto a poner dinero en esa optimización que está presente. En cuanto al tiempo de CPU, mi punto de referencia muestra lo contrario, dado que solo está en FF 3.5.6. –

+0

Usted está por supuesto, correcto, pero no quise (significa que) que la capacidad aumentaría con cada inserción ... –

-3

Hasta donde sé, no hay ningún beneficio porque en realidad no hay diferencia alguna entre esto y una "matriz regular", excepto la sintaxis de creación: todos los objetos de Javascript son matrices asociativas.

+0

No exactamente. Los objetos/matrices asociativas no tienen una propiedad 'length', razón por la cual la biblioteca Closure lo está agregando en este caso (por ejemplo:' console.log ({}. Length, [] .length) '=>" undefined 0 "). –

+0

Además, las matrices ([]) tienen un conjunto más amplio de métodos (vea Array.prototype). – Nickolay

+0

Las matrices extienden objetos (matrices asociativas); agrega todos los métodos de matriz, además realiza un seguimiento de la longitud. Tenga en cuenta que 'length' no hace un seguimiento de los objetos no enteros. Es PHP que son exactamente lo mismo –

2

En este caso, mi conjetura sería que arrayLike[len++] = el es una optimización sobre actualArray.push(el). Sin embargo, después de hacer un punto de referencia simple (código proporcionado debajo de los resultados), parece que este método es realmente más lento que usar una matriz estándar usando el método push, así como con la misma técnica de construcción.

resultados (de OS X 10.5.8, FF 3.5.6) *:

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

En conclusión, ¿por qué El cierre se utiliza una matriz asociativa en este caso es más allá de mí. Probablemente haya una razón (por ejemplo, esta técnica puede funcionar mejor en Chrome, o de forma menos dudosa, esta técnica puede tener un mejor rendimiento en futuras versiones de motores de JavaScript en general), pero no veo una buena razón en este caso.

* No se proporcionó una media porque los tiempos variaron desde la ejecución de la prueba hasta la ejecución de la prueba, pero siempre dieron como resultado el mismo orden. Si estás interesado, puedes llevarlo a cabo por tu cuenta.

código de referencia:

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

El tamaño y tipo de value es insignificante para la prueba como JavaScript utiliza copy-on-write. Se utilizó un gran (1kb) value con el propósito de convencer a los lectores que no están familiarizados con esta característica de JavaScript.

+2

Al hacer un benchmark, recuerde comparar con IE 6. Una gran cantidad de hacks de rendimiento extraño que puede estar presente para trabajar con algunas características de rendimiento horrible de IE 6, mientras que proporciona poco o ningún beneficio en otros navegadores. –

+0

De hecho, soy consciente de eso. Pero, como dije, estoy en una Mac en este momento y no puedo probar en una amplia gama de navegadores. –

+0

Exactamente, Brian. Y tendría sentido favorecer IE sobre el rendimiento debido a la cuota de mercado del navegador (hay muchos enemigos por ahí, pero los hechos son hechos). Además, asegúrese de ejecutar esta prueba con el motor V8 de Google. –

0

No veo ninguna diferencia ya que alert(typeof []); devuelve "objeto". Cada vez que veo algo como esto (especialmente del Goog), tengo que asumir que es para aumentar el rendimiento.

Es esencialmente la devolución de una matriz sin todas las funciones heredadas como push y pop.

+0

Pensé lo mismo también. Resulta que ambos estábamos equivocados. Ver mi punto de referencia –

+0

En realidad, la última vez que revisé, Google Closure no fue optimizado para el rendimiento en absoluto, y se parecía más a un código heredado. – kangax

0
goog.dom.getElementsByTagNameAndClass_ 

Se trata de colecciones html. Un objeto arrayLike es una instantánea de una lista de nodos, en lugar del objeto de colección 'activo'. Lo hace tan fácil como trabajar con una matriz indexada, y es menos probable que cause complicaciones si crea o elimina nodos al pasar por sus miembros.

2

Creo que este ejemplo crea un objeto similar a una matriz en lugar de una matriz real, porque otros métodos DOM también devuelven objetos parecidos a una matriz (NodeList).

uso consistente de "matriz-le gusta" en la API obliga al desarrollador para evitar el uso de los métodos específica de la matriz (utilizando goog.array lugar), por lo que son menos propensas a error cuando alguien más adelante decide cambiar de una llamada getElementsByTagNameAndClass a, por ejemplo, getElementByTagName .