Número de teclas de matriz y & # 8220; número & # 8221; inesperadamente se consideran lo mismo

StackOverflow https://stackoverflow.com/questions/453765

Pregunta

He estado jugando con matrices de JavaScript y me he encontrado con 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 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 se agregan estas propiedades a una matriz me parece inconsistente.

Antes de continuar, permítanme observar cómo los valores de cadena se convertirán en valores numéricos en javascript.

  • Cadena no vacía - > Valor numérico de cadena o NaN

  • Cadena vacía - > 0 0

Entonces, dado que una matriz de JavaScript es un objeto, lo siguiente es legal:


myArray["someThing"] = "someThing";
myArray[""] = "Empty String";
myArray["4"] = "four";

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] = four
myArray[someThing] = someThing
myArray[] = Empty String
1, 2, 3, 4, four

El resultado es inesperado.

La cadena no vacía " 4 " se convierte en su valor numérico al establecer 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. También la cadena no vacía " algo " no se convierte a su valor numérico, NaN, se trata como una cadena. Entonces cual es? ¿está 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 se incluyen en myArray.length y myArray.join (" ;, ")?

¿Fue útil?

Solución

Las claves de una matriz de JavaScript son en realidad cadenas. Para obtener detalles y una implementación de un tipo de mapa para claves arbitrarias, consulte 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 también se convierten en cadenas antes de que pueda suceder algo más. Un nombre de propiedad P se tratará como un índice de matriz (es decir, se invocará la magia de matriz 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 & # 8722; 1

Los índices numéricos que se convertirán en cadenas (y no al revés) se pueden 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 : puede obtener resultados inesperados si alguien se mete con algunos prototipos (y tampoco es realmente rápido). Utilice el estándar para (var i = 0; i < array.length; ++ i) en su lugar.

Otros consejos

(editar: lo siguiente no es del todo correcto)

Las claves de un Objeto de JavaScript son en realidad cadenas. Un Array de Javascript por sí solo 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 número entero no negativo (por ejemplo, es alfanumérico, negativo o un número de coma flotante con una fracción), fallará en el almacén de índice de matriz y, por defecto, el objeto (que es la clase base de Array), 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 ( longitud, unión, corte, empalme, empuje, pop, etc.).

editar: lo anterior no es del todo correcto (como muestra el ejemplo foo / bar / baz de Christopher). Los índices de almacenamiento reales de acuerdo con la especificación ECMAscript están, en de hecho, cadenas, pero si son índices de matriz válidos (enteros no negativos), entonces el método [[Put]] del objeto Array, que es especial, hace que esos valores particulares sean visibles para la matriz '' array-ish '' ; métodos.

Esta es una respuesta a publicación de PhiLo . Su punto de referencia es defectuoso porque usa diferentes nombres de propiedades para la versión del objeto: debería haber usado también i y no x .

Si se hace correctamente, por ejemplo, así:

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 estarán muy cerca. En FF3.0.5, la versión de matriz es incluso más lenta (en Opera, es al revés).

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, está agregando una propiedad al objeto en lugar de agregarla a la matriz. Lo mismo ocurre con la cadena vacía, aunque no puede usar la notación de punto para una cadena vacía ... extraño, ¿eh?

En el caso de "4", se puede convertir en un entero y, por lo tanto, se utiliza como índice en la matriz.

No estoy de acuerdo con Christoph cuando dice "los índices de matriz se convierten en cadenas".

Primero, creo que depende de la implementación ... Supongo que los implementadores (buenos) 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-puntos de referencia (es decir, no es súper 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 matriz: 83 Con objeto: 226
En Safari 3.1, obtengo: Con matriz: 140 Con objeto: 313
En Opera 9.26, por alguna razón no obtengo el resultado, pero si reduzco a la décima parte del número de bucles, obtengo: Con matriz: 47 Con objeto: 516
En realidad, dejé que Opera se ejecutara mientras escribía esto, y finalmente obtuve el resultado: Con matriz: 281 Con objeto: 166063 ...

¡Entonces los arreglos están optimizados! 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 son tratadas como tales, que van junto con la fórmula citada ...

Entonces, mi interpretación de sus 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 matriz: 92 Con matriz s: 93 Con objeto (i): 243 Con objeto s: 194 Con objeto i: 125 (los rendimientos varían entre ejecuciones, pero son más o menos consistentes).

No estoy súper convencido de este número entero - > cadena - > entero de ida y vuelta, ni siquiera que ECMA solicite esta secuencia. La forma en que lo leo es: es la propiedad es una cadena y se puede interpretar como un entero, luego se trata como tal.

Por supuesto, la única forma segura de saber sería mirar una implementación ...

Noto con interés que los objetos simples que obtienen una propiedad entera o una propiedad que puede transformarse en un entero de alguna manera están optimizados. Quizás porque muchos programadores de JS usaron objetos simples como matrices, por lo que los implementadores consideraron interesante optimizar este caso.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top