¿Deberían los índices negativos en las matrices de JavaScript contribuir a la longitud de la matriz?

84

En javascript defino una matriz como esta

var arr = [1,2,3];

también puedo hacer

arr[-1] = 4;

Ahora si lo hago

arr = undefined;

También pierdo la referencia al valor en arr [-1] .

Entonces, para mí, lógicamente, parece que arr [-1] también es parte de arr .

Pero cuando sigo (sin establecer arr en indefinido)

arr.length;

Devuelve 3, no 4 ;

Entonces, mi punto es que si las matrices se pueden usar con índices negativos, estos índices negativos también deberían ser parte de su longitud **. No sé, puede que me equivoque o que me falte algún concepto sobre las matrices.

me_digvijay
fuente
¿Por qué querría utilizar un índice negativo? No veo el punto a menos que me esté perdiendo algo.
elclanrs
11
También puedes escribir arr[1.5] = 1y eso tampoco afecta la longitud. La especificación del lenguaje es muy clara sobre lo que afecta la longitud. Puede que no te guste, pero tienes que vivir con ello. O eso o diseña tu propio lenguaje competitivo y convence a la gente para que lo cambie.
Raymond Chen
1
@DigvijayYadav: podría considerarse una falla o una característica como muchas otras cosas en JavaScript. Esto se debe a que las matrices se comportan un poco como objetos, también puede usar una cadena, var a = []; a['foo'] = 'baz'pero eso no significa que deba hacerlo; está claramente en contra de todas las convenciones.
elclanrs
2
Chicos, el punto principal aquí es que los ARRAYS SON OBJETOS. No hay diferencia. Esa es la razón de este comportamiento, y es completamente intencional, incluso si no te gusta. Espere hasta que sepa que TODOS los números se representan como punto flotante, incluso enteros. JavaScript es un lenguaje curioso ...
Tony R
2
Puede encontrar detalles del algoritmo que define la configuración de los elementos de la matriz en la especificación: es5.github.com/#x15.4.5.1 . ( Especialmente el paso 4) y el "índice de la matriz" se define aquí: es5.github.com /#x15.4 .
Felix Kling

Respuestas:

109

Entonces, para mí, lógicamente, parece que arr [-1] también es parte de arr.

Sí, lo es, pero no de la manera que usted cree.

Puede asignar propiedades arbitrarias a una matriz (como cualquier otro Objeto en JavaScript), que es lo que está haciendo cuando "indexa" la matriz -1y asigna un valor. Dado que este no es un miembro de la matriz y solo una propiedad arbitraria, no debe esperar lengthconsiderar esa propiedad.

En otras palabras, el siguiente código hace lo mismo:

var arr = [1, 2, 3];

​arr.cookies = 4;

alert(arr.length) // 3;
Andrew Whitaker
fuente
31
Ok, significa que los índices negativos en realidad no actúan como índices reales.
me_digvijay
4
Correcto, son solo propiedades arbitrarias en la matriz.
Andrew Whitaker
Pero, ¿qué pasa si quiero usar índices positivos como propiedades que no son como índices y no quiero que contribuyan a la longitud de la matriz?
me_digvijay
7
Entonces no use una matriz, use un objeto simple. Pero en ese caso perderá la lengthpropiedad incorporada .
Andrew Whitaker
2
@Digvijay: La consola solo muestra las propiedades con nombres de números enteros positivos porque (supongo) solo hace un forciclo ordinario de 0a .length. Haz console.dir(arr)para ver el objeto completo. Además, el uso de la notación entre corchetes para acceder a las matrices no es una característica de las matrices, es una característica inherente de los objetos ( var obj = {foo: 'bar'}; obj.foo or obj['foo']).
Felix Kling
25

La lengthpropiedad devolverá un número uno más alto que el "índice" asignado más alto, donde los "índices" de Array son números enteros mayores o iguales a cero. Tenga en cuenta que JS permite matrices "dispersas":

var someArray = [];
someArray[10] = "whatever";
console.log(someArray.length); // "11"

Por supuesto, si no existen elementos que a continuación lengthes 0. Tenga en cuenta también que lengthno se actualiza si usa deletepara eliminar el elemento más alto.

Pero las matrices son objetos, por lo que puede asignar propiedades con otros nombres de propiedad arbitrarios, incluidos números negativos o fracciones:

someArray[-1] = "A property";
someArray[3.1415] = "Vaguely Pi";
someArray["test"] = "Whatever";

Tenga en cuenta que, detrás de escena, JS convierte los nombres de propiedad en cadenas incluso cuando proporciona un número como -1. (Los índices enteros positivos también se convierten en cadenas, para el caso).

Métodos de matriz, como .pop(), .slice(), etc., sólo el trabajo sobre los-o de mayor cero enteros "índices", y no en otras propiedades, por lo que lengthes coherente en ese punto.

nnnnnn
fuente
Gracias, tu respuesta ayudó mucho. También probé el método de empalme con índice negativo, encontré que si uso -1 va al último elemento de la matriz, porque -2 va al penúltimo elemento y así sucesivamente.
me_digvijay
+1. La longitud no te dice cuántos elementos hay ni cuántas propiedades hay. Es solo el índice más alto + 1. Por ejemplo var a = new Array(9), dado , atiene una longitud de 9 y no tiene ningún elemento.
RobG
1
@DigvijayYadav - Sí, se supone que el Array.slice()método hace eso. El String.slice()método hace lo mismo.
nnnnnn
7

Tenga en cuenta que cuando usa un índice de posición (o 0), los valores se colocan dentro de la matriz:

var array = [];

array[0] = "Foo";
array[1] = "Bar";

// Result: ["Foo", "Bar"]
// Length: 2

Este no es el caso cuando agrega valores que no son índices (no 0-9 +):

var array = [];

array[0]  = "Foo";
array[1]  = "Bar";
array[-1] = "Fizzbuzz"; // Not a proper array index - kill it

// Result: ["Foo", "Bar"]
// Length: 2

Los valores solo se colocan en la matriz cuando se sigue las reglas. Cuando no lo hace, no se aceptan. Sin embargo, se aceptan en el objeto Array en sí, que es el caso de casi cualquier cosa en JavaScript. Aunque ["Foo", "Bar"]son los únicos valores en nuestra matriz, aún podemos acceder a "Fizzbuzz":

array[-1]; // "Fizzbuzz"

Pero tenga en cuenta nuevamente que esto no es parte de los valores de la matriz, ya que su "índice" no es válido. En cambio, se agregó a la matriz como un miembro más. Podríamos acceder a otros miembros de la matriz de la misma manera:

array["pop"]; // function pop() { [native code] }

Tenga en cuenta aquí que estamos accediendo al popmétodo en la matriz, que nos informa que contiene código nativo. No estamos accediendo a ninguno de los valores de la matriz con una clave de "pop", sino a un miembro del objeto de la matriz en sí. Podemos confirmar esto aún más pasando por encima de los miembros públicos del objeto:

for (var prop in array) 
    console.log(prop, array[prop]);

Que escupe lo siguiente:

 0 Foo
 1 Bar
-1 Fizzbuzz

De nuevo, está en el objeto , pero no en la matriz .

¡Impresionante pregunta! Seguro que me hizo pensar dos veces.

Sampson
fuente
1
+1 Incluso la especificación establece que las propiedades con un nombre de propiedad numérico positivo se denominan elementos de la matriz (y cualquier otra propiedad no lo es): es5.github.com/#x15.4 .
Felix Kling
El resultado no es: ["Foo", "Bar"]pero ["Foo", "Bar", -1: "Fizzbuzz"]marque el violín aquí . Como escribiste, se convierte en una propiedad con la clave -1 pero definitivamente es visible en la salida. De lo contrario, su respuesta es agradable y completa. Si cambia, votaré a favor de nuevo, mi voto en contra fue demasiado duro, pero no puedo eliminarlo; el tiempo expiró.
March
5

Si realmente desea que se incluyan índices negativos y otros índices en la longitud, cree la funcionalidad usted mismo:

function getExtendedArray() {
    var a = [];

    Object.defineProperty(a, 'totalLength', { 
        get : function() { return Object.keys(a).length; }
    });

    return a;
}

Entonces por ejemplo:

var myArray = getExtendedArray();
console.log(myArray.totalLength); // 0
myArray.push("asdf");
myArray[-2] = "some string";
myArray[1.7777] = "other string";
console.log(myArray.totalLength); // 3
David Sherret
fuente
Y si desea que la longitud incluya solo los índices / propiedades numerados , puede agregar una declaración if para la clave. if(Number(key) != NaN) length++;Si desea filtrar los flotantes, agregue && key % 1 == 0a la condición
Bonnev
4

Array es solo un tipo especial de objeto en JS. Hay una sintaxis especial, que es buena, porque nos permite trabajar con ellos de forma eficaz. Imagínese lo tedioso que sería escribir un literal de matriz de esa manera:

const array = {0: 'foo', 1: 'bar', 2: 'baz'} //etc...

Sin embargo, siguen siendo objetos . Entonces, si lo hace:

const myArray = [42, 'foo', 'bar', 'baz'];

myArray[-1] = 'I am not here';
myArray['whatever'] = 'Hello World';

Esas propiedades que no son enteras se adjuntarán a un objeto (qué matriz es), pero algunos métodos nativos como Array.length.

Puede asumir que el método nativo de 'longitud' es un captador que cuenta todas las propiedades (y los índices son propiedades, mientras que los valores son los valores reales ) convertibles en números enteros positivos o cero. Algo como esta pseudo-implementación:

Debido a las especificaciones , el lengthmétodo nativo , como getter ( exampleArray.length), verificará todas las claves ' enteros no negativos menores de 2 32 ', obtendrá la mayor y devolverá su valor numérico + 1.

Como setter ( exampleArray.length = 42), creará algunos espacios vacíos para los índices 'perdidos' si la longitud dada es mayor que el número real de claves enteras no negativas, o eliminará todas las claves enteras no negativas (y sus valores) que no sean mayores que el longitud dada.

La pseudo-implementación:

const myArray = {
  '-1': 'I am not here', // I have to use apostrophes to avoid syntax error
  0: 42,
  1: 'foo',
  2: 'bar',
  3: 'baz',
  whatever: 'Hello World',

  get myLength() {
    let highestIndex = -1;
    for (let key in this) {
      let toInt = parseInt(key, 10);
      if (!Number.isNaN(toInt) && toInt > -1) {
        // If you're not an integer, that's not your business! Back off!
        if (toInt > highestIndex) highestIndex = toInt;
      }
    }
    return highestIndex + 1;
  },
  set myLength(newLength) {
    /* This setter would either:
      1) create some empty slots for 'missing' indices if the given length is greater than the actual number of non-negative-integer keys
      2) delete all non-negative-integer keys (and their values) which are not greater then the given length.
    */
  }
}

console.log(myArray.myLength); // 4

myArray[9] = 'foobar';

console.log(myArray.myLength); // 10

HynekS
fuente
3

Las matrices en JavaScript son en realidad objetos. Simplemente se crean un prototipo del constructor Array.

Los índices de matriz son en realidad claves en un mapa hash, y todas las claves se convierten en cadenas. Puede crear cualquier clave (es decir, "-1"), pero los métodos en Array están diseñados para actuar como una matriz. Entonces, lengthno es el tamaño del objeto, solo se garantiza que sea más grande que el índice entero más grande. De manera similar, la impresión arrsolo mostrará valores con claves enteras> = 0.

Más info aquí.

Tony R
fuente
exactamente. o podríamos empezar a preguntarnos por qué array ['foo'] no es un índice válido - javascript te permite hacerlo, pero no significa que la definición de lo que un array (y sus elementos) cambie de un idioma a otro. trabajando según lo previsto.
tw airball
De hecho, las "matrices" no existen en JS. Pero las "matrices asociativas", también conocidas como "objetos", están en el núcleo de JS. Así que no fue un gran salto hacer un objeto Array que imita una matriz regular.
Tony R
1
Hay una diferencia muy especial entre matrices y objetos simples: la propiedad de longitud autoajustable.
RobG
1

Según MDN:

El valor de la propiedad length es un número entero con signo positivo y un valor menor que 2 elevado a 32 (232)

En Javascript, puede establecer una propiedad en cualquier objeto que cree.

var array = new Array();
array = [1,2,3];
array["boom"] = "pow";

De la misma manera, cuando establece un índice negativo, lo almacena como una propiedad en la matriz en lugar de como parte del índice.

array[-1] = "property does not add to array length";

Es por eso que la longitud no lo refleja, pero un bucle for..in lo muestra.

ktilcu
fuente
-1

Cuando utiliza índices no positivos o no numéricos, la matriz se comporta como una matriz asociativa que tiene pares clave-valor.

Para iterar a través de la matriz, puede usar

for(var index in myArray) {
  document.write( index + " : " + myArray[index] + "<br />");
}
Ashán
fuente