Skip to content

Instantly share code, notes, and snippets.

@vstarck
Forked from anonymous/array.length.md
Created January 4, 2013 03:31
Show Gist options
  • Save vstarck/4449687 to your computer and use it in GitHub Desktop.
Save vstarck/4449687 to your computer and use it in GitHub Desktop.

Sea:

var a = [];

Dice la especificación de Javascript que los Arrays son simplemente unos objetos un poco curiosos y más o menos simpáticos. Tienen una propiedad especial llamada length y si le intentamos añadir una propiedad con un nombre que sea un número válido (uint32) entonces lo consideran un "índice" y tratan ese valor como un elemento de un array/vector/llámaloX que mantiene internamente.

La propiedad length es especial porque se actualiza más allá de nuestro control cuando ocurren algunas cosas. En concreto lo que dice la especificación es son las siguientes 2 cosas: Cada vez que se añada al array un elemento, es decir, cito (énfasis mío): "una propiedad cuyo nombre es un índice válido de array, si es necesario, length se actualizará a ser 1 más que ese índice". Además, cuando se modifique length, para mantener la restricción, "cualquier propiedad cuyo nombre es un índice válido que no es menor que el nuevo length, se borra automáticamente".

Planteémos los 2 casos:

Caso 1.
a.length = 10;

Al hacer esto, no se crea nada ni se borra nada. Sólo se actualiza el valor de length. La restricción se sigue cumpliendo y es válida, luego el array es correcto y todo está bien. (Puedes ver el código de V8, SpiderMonkey, etc para asegurarse si quieres)

¿Cuál es el problema? El problema es que el resto de métodos de Array (básicamente join, ya que toString se apoya normalmente en join) se apoyan en length para devolver una representación del array. Es decir, la consola de Firefox o de Chrome no te engaña (VER NOTA AL FINAL), pero te está mostrando una cosa distinta a la que quieres ver. Lo que quieres ver es si realmente se ha creado una propiedad de nombre '0', '1', '2', etc. Para eso, nada más fácil que hacer:

3 in a

Y la consola dirá:

false

Si queremos de verdad comprobar que ese es el caso, podemos hacerlo fácilmente:

var a = [];
a.length = 10;
a[1] = undefined;
a[4] = undefined;

Ahora pedimos a y toString parece engañarnos (simplemente muestra otra cosa). Si preguntamos:

3 in a

dirá false, pero si preguntamos:

4 in a

dirá true, porque sí, tiene valor undefined pero hemos creado realmente la propiedad con nombre '4'.

NOTA: Si hacemos a = new Array(10); cambia porque estamos creando un objeto nuevo. Pero aún así, el constructor Array tampoco crea las propiedades con nombre que es índice válido blabla... los elementos:

a = new Array(10);
3 in a // -> false
Caso 2.

Si en lugar de lo anterior hacemos cualquiera de estas cosas:

a = [undefined, undefined, undefined, undefined,undefined, undefined, undefined, undefined,undefined, undefined];
// o:
a.push(undefined); // diez veces;

En el primer caso estamos creando un Array nuevo, y a ese le estamos metiendo explícitamente 10 elementos. En el otro caso, en cada push: a. buscamos la length actual, b. añadimos un elemento cuyo nombre es el que toca según length y cuyo valor es undefined, c. actualizamos length para que siga cumpliendo la restricción. (El caso de hacer un push múltiple es igual, internamente hace casi lo mismo diez veces.

Y el problema? Pues que lo que nos saca toString en este caso es igual que lo que saca en el anterior. Pero como digo es un problema de mirar una cosa cuando lo que queremos mirar es otra.

NOTA AL FINAL: De hecho, la nueva consola de Firefox es en esto (y en otras muchas cosas) mejor que la de Chrome. Cuando hacemos explícitamente a[4] = undefined podemos distinguirlo de cuando simplemente no se ha definido. Me dice la consola:

> var a = []; a.length = 10;
[23:33:00.116] 10
> a
[23:33:02.125] [, , , , , , , , , ,]
--
> a[4] = undefined;
[23:33:13.314] undefined
> a
[23:33:15.719] [, , , , (void 0), , , , , ,]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment