objetos - matrices de JavaScript e incoherencias de conversión de tipo
recorrer array de objetos javascript (5)
(editar: el siguiente no es del todo correcto)
Las claves de un objeto JavaScript son en realidad cadenas. Una matriz Javascript por sí misma tiene índices numéricos. Si almacena algo con un índice que puede interpretarse como un entero no negativo, intentará hacerlo. Si almacena algo con un índice que no es un entero no negativo (por ejemplo, es alfanumérico, negativo o un número de coma flotante con una pieza fraccionaria), fallará en el almacén de índices de matriz, y de forma predeterminada en el Objeto (que es Clase base de Array) store, que luego convierte el argumento en una cadena y almacena por índice de cadena, pero estas propiedades almacenadas no son vistas por la clase Array y por lo tanto no son visibles para sus métodos / propiedades (length, join, slice, splice , push, pop, etc.).
editar: lo anterior no es del todo correcto (como muestra el ejemplo foo / bar / baz de Christopher). Los índices de almacenamiento reales según la especificación ECMAscript son, de hecho, cadenas, pero si son índices de matrices válidos (enteros no negativos), entonces el método [[Put]]
del objeto Array, que es especial, hace que esos valores particulares sean visibles para el Array métodos "array-ish".
He estado jugando con matrices de JavaScript y me he topado con, lo que siento son algunas inconsistencias, espero que alguien pueda explicarlas por mí.
Comencemos con esto:
var myArray = [1, 2, 3, 4, 5];
document.write("Length: " + myArray.length + "<br />");
for( var i in myArray){
document.write( "myArray[" + i + "] = " + myArray[i] + "<br />");
}
document.write(myArray.join(", ") + "<br /><br />");
Length: 5 myArray[0] = 1 myArray[1] = 2 myArray[2] = 3 myArray[3] = 4 myArray[4] = 5 1, 2, 3, 4, 5
No hay nada de especial en este código, pero entiendo que una matriz de JavaScript es un objeto, por lo que las propiedades se pueden agregar a la matriz, la forma en que estas propiedades se agregan a una matriz me parece inconsistente.
Antes de continuar, permítanme observar cómo los valores de cadena deben convertirse a valores numéricos en javascript.
Cuerda no vacía -> Valor numérico de la cuerda o NaN
Cadena vacía -> 0
Entonces, como una matriz de JavaScript es un objeto, lo siguiente es legal:
for( var i in myArray){
document.write( "myArray[" + i + "] = " + myArray[i] + "<br />");
}
document.write(myArray.join(", ") + "<br /><br />");
myArray["someThing"] = "someThing";
myArray[""] = "Empty String";
myArray["4"] = "four";
Length: 5 myArray[0] = 1 myArray[1] = 2 myArray[2] = 3 myArray[3] = 4 myArray[4] = four myArray[someThing] = someThing myArray[] = Empty String 1, 2, 3, 4, four
La salida es inesperada.
La cadena no vacía "4" se convierte en su valor numérico al configurar la propiedad myArray ["4"], esto parece correcto. Sin embargo, la cadena vacía "" no se convierte en su valor numérico, 0, se trata como una cadena vacía. Además, la cadena no vacía "algo" no se convierte a su valor numérico, NaN, se trata como una cadena. Entonces, ¿cuál es? es la declaración dentro de myArray [] en contexto numérico o de cadena?
Además, ¿por qué las dos propiedades no numéricas de myArray no están incluidas en myArray.length y myArray.join (",")?
Las matrices, como todo lo demás en JavaScript, son objetos. Los objetos han sido bendecidos con la notación de puntos para aliviar la carga de los desarrolladores. Usando tu ejemplo
myArray["someThing"] = "someThing";
es lo mismo que escribir
myArray.someThing = "someThing";
En este caso, agrega una propiedad al objeto en lugar de agregarlo a la matriz. Lo mismo ocurre con la cadena vacía, aunque no puedes usar la notación de puntos para una cadena vacía ... extraño, ¿eh?
En el caso de "4", se puede forzar en un número entero y, por lo tanto, se utiliza como índice en la matriz.
No estoy de acuerdo con Christoph cuando afirma que "los índices de matriz se convierten en cadenas".
En primer lugar, creo que depende de la implementación ... Supongo que (bueno) los implementadores optimizarán el acceso a la matriz, hay algunas formas inteligentes de hacerlo.
En realidad, hice una pequeña prueba, y aunque es tan buena como la mayoría de los micro-benchmarks (es decir, no es muy confiable), es interesante:
result = ""
var x;
var trueArray = []
var startTime = new Date();
for (var i = 0; i < 100000; i++)
{
x = "i" + i; // To do the same operations
trueArray[i] = 1;
}
var endTime = new Date();
result += "With array: " + (endTime - startTime) + "/n";
var sArray = []
var startTime = new Date();
for (var i = 0; i < 100000; i++)
{
x = "" + i;
sArray[x] = 1;
}
var endTime = new Date();
result += "With s array: " + (endTime - startTime) + "/n";
var objArray = {}
var startTime = new Date();
for (var i = 0; i < 100000; i++)
{
x = "i" + i;
objArray[x] = 1;
}
var endTime = new Date();
result += "With object(i): " + (endTime - startTime) + "/n";
var sobjArray = {}
var startTime = new Date();
for (var i = 0; i < 100000; i++)
{
x = "" + i;
sobjArray[x] = 1;
}
var endTime = new Date();
result += "With s object: " + (endTime - startTime) + "/n";
var iobjArray = {}
var startTime = new Date();
for (var i = 0; i < 100000; i++)
{
x = "" + i;
iobjArray[i] = 1;
}
var endTime = new Date();
result += "With i object: " + (endTime - startTime) + "/n";
// Then display result
En IE6, obtengo: Con matriz: 1453 Con objeto: 3547
En FF 3.0, obtengo: Con array: 83 Con objeto: 226
En Safari 3.1, obtengo: Con array: 140 Con objeto: 313
En Opera 9.26, por alguna razón no obtengo el resultado, pero si reduzco al décimo de número de loops, obtengo: Con array: 47 Con objeto: 516
En realidad, dejé que Opera funcionara mientras escribo esto, y finalmente obtuve el resultado: Con array: 281 Con objeto: 166063 ...
¡Entonces las matrices están optimizadas! Lo cual es afortunado ...
La demostración de Christoph no me impresionó. Mi conclusión sería más que las cadenas que se pueden interpretar como números se tratan como tales, que van junto con la fórmula citada ...
Así que mi interpretación de los resultados es que la matriz se comporta como una matriz rápida con índices numéricos cuando se alimenta con estos (con quizás un comportamiento de matriz asociativa en valores dispersos, es decir, algunos índices grandes aislados), pero como un objeto, todavía tiene el manejo normal de las propiedades. Pero estas propiedades no se manejan en la parte de la matriz, de ahí el resultado que obtuvo con join ().
[EDITAR] Agregué algunos bucles, siguiendo la idea de Christoph.
En FF3, obtengo: con array: 92 con s array: 93 con objeto (i): 243 con s objeto: 194 con objeto i: 125 (los perfs varían entre ejecuciones, pero son más o menos consistentes).
No estoy súper convencido de este entero -> cadena -> entero de ida y vuelta, ni siquiera que ECMA solicite esta secuencia. La forma en que lo leo es: la propiedad es una cadena y se puede interpretar como un número entero, luego se trata como tal.
Por supuesto, la única manera segura de saber sería mirar una implementación ...
Observé con interés que los objetos simples que obtienen una propiedad entera o una propiedad que puede transformarse en un entero se optimizan de alguna manera. Tal vez porque muchos programadores de JS usaron objetos simples como matrices, por lo que los implementadores consideraron interesante optimizar este caso.
Esta es una respuesta a la publicación de PhiLo . Su punto de referencia es defectuoso porque usa diferentes nombres de propiedad para la versión del objeto: debería haber usado i
también y no x
.
Si se hace correctamente, por ejemplo, de esta manera:
var start, end, count = 1000000;
var obj = {},
array = [];
start = new Date;
for(var i = count; i--; )
array[i] = i;
end = new Date;
document.writeln(Number(end) - Number(start));
start = new Date;
for(var i = count; i--; )
obj[i] = i;
end = new Date;
document.writeln(Number(end) - Number(start));
Verás que los tiempos serán muy cercanos. En FF3.0.5, la versión de matriz es incluso consistentemente más lenta (en Opera, es al revés).
Las claves de una matriz de JavaScript son en realidad cadenas. Para detalles y una implementación de un tipo de mapa para claves arbitrarias, verifique esta respuesta .
Para aclarar y agregar a lo que Jason publicó: las matrices de JavaScript son objetos. Los objetos tienen propiedades. Un nombre de propiedad es un valor de cadena. Por lo tanto, los índices de matriz se convierten en cadenas también antes de que ocurra algo más. Un nombre de propiedad P se tratará como un índice de matriz (es decir, se invocará la matriz mágica especial) si se cumple lo siguiente (ECMA-262, 15.4):
ToString (ToUint32 (P)) es igual a P y ToUint32 (P) no es igual a 2 ^ 32 - 1
Que los índices numéricos se conviertan en cadenas (y no al revés) se puede verificar fácilmente:
var array = [];
array[1] = ''foo'';
array[''1''] = ''bar'';
array[''+1''] = ''baz'';
document.writeln(array[1]); // outputs bar
Además, es una mala práctica iterar sobre las entradas de una matriz con un bucle for..in
: es posible que obtengas resultados inesperados si alguien se equivoca con algunos prototipos (y tampoco es muy rápido). Use el estándar for(var i= 0; i < array.length; ++i)
lugar.