Gli indici negativi negli array JavaScript dovrebbero contribuire alla lunghezza dell'array?


85

In javascript definisco un array come questo

var arr = [1,2,3];

anche io posso farlo

arr[-1] = 4;

Ora se lo faccio

arr = undefined;

Perdo anche il riferimento al valore in arr [-1] .

Quindi per me logicamente sembra che anche arr [-1] sia una parte di arr .

Ma quando seguo (senza impostare arr su undefined)

arr.length;

Restituisce 3 non 4 ;

Quindi il mio punto è che se gli array possono essere usati con indici negativi, anche questi indici negativi dovrebbero far parte della loro lunghezza **. Non so, potrei sbagliarmi o potrei perdere qualche concetto sugli array.


Perché dovresti utilizzare un indice negativo? Non vedo il punto a meno che non mi perda qualcosa.
elclanrs

11
Puoi anche scrivere arr[1.5] = 1e anche questo non influisce sulla lunghezza. La specifica della lingua è molto chiara su ciò che influisce sulla lunghezza. Potrebbe non piacerti ma devi conviverci. O quello o progetta il tuo linguaggio competitivo e convinci le persone a passare ad esso.
Raymond Chen

1
@DigvijayYadav: potrebbe essere considerato un difetto o una funzionalità come molte altre cose in JavaScript. Questo perché gli array si comportano un po 'come gli oggetti, puoi anche usare una stringa var a = []; a['foo'] = 'baz'ma questo non significa che dovresti; è chiaramente contro tutte le convenzioni.
elclanrs

2
Ragazzi, il punto principale qui è che GLI ARRAY SONO OGGETTI. Non c'è differenza. Questa è la ragione di questo comportamento, ed è completamente intenzionale anche se non ti piace. Aspetta solo di apprendere che TUTTI i numeri sono rappresentati come virgola mobile, anche interi. JavaScript è un linguaggio curioso ...
Tony R

2
Puoi trovare i dettagli dell'algoritmo che definisce l'impostazione degli elementi dell'array nella specifica: es5.github.com/#x15.4.5.1 . ( specialmente il passaggio 4) e "indice dell'array" è definito qui: es5.github.com /#x15.4 .
Felix Kling,

Risposte:


111

Quindi per me logicamente sembra che anche arr [-1] sia una parte di arr.

Sì, lo è, ma non nel modo in cui pensi che sia.

Puoi assegnare proprietà arbitrarie a un array (proprio come qualsiasi altro oggetto in JavaScript), che è ciò che stai facendo quando "indicizzi" l'array in -1e assegni un valore. Poiché questo non è un membro della matrice e solo una proprietà arbitraria, non dovresti aspettarti lengthdi considerare quella proprietà.

In altre parole, il codice seguente fa la stessa cosa:

var arr = [1, 2, 3];

​arr.cookies = 4;

alert(arr.length) // 3;

31
Ok, significa che gli indici negativi in ​​realtà non si comportano come gli indici reali.
me_digvijay

4
Esatto, sono solo proprietà arbitrarie sull'array.
Andrew Whitaker

Ma cosa succede se voglio utilizzare indici positivi come proprietà diverse dagli indici e non voglio che contribuiscano alla lunghezza dell'array?
me_digvijay

7
Quindi non usare un array, usa un semplice oggetto. Ma in quel caso perderai la lengthproprietà costruita .
Andrew Whitaker

2
@Digvijay: la console mostra solo le proprietà con nomi interi positivi perché (presumo) fa solo un normale forciclo da 0a .length. Fare console.dir(arr)per vedere l'oggetto completo. Inoltre, l'uso della notazione tra parentesi per accedere agli array non è una caratteristica degli array, è una caratteristica intrinseca degli oggetti ( var obj = {foo: 'bar'}; obj.foo or obj['foo']).
Felix Kling

25

La lengthproprietà restituirà un numero uno superiore all '"indice" assegnato più alto, dove gli "indici" di array sono numeri interi maggiori o uguali a zero. Nota che JS consente array "sparsi":

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

Ovviamente se non ci sono elementi allora lo lengthè 0. Nota anche che lengthnon viene aggiornato se utilizzi deleteper rimuovere l'elemento più alto.

Ma gli array sono oggetti, quindi puoi assegnare proprietà con altri nomi di proprietà arbitrari inclusi numeri o frazioni negativi:

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

Nota che dietro le quinte JS converte i nomi delle proprietà in stringhe anche quando fornisci un numero come -1. (Gli indici interi positivi diventano anche stringhe, se è per questo.)

Metodi di matrice, come .pop(), .slice()ecc, solo lavoro sui zero o superiore-intero "indici", non su altre proprietà, così lengthè coerente su tale punto.


Grazie la tua risposta ha aiutato molto. Inoltre ho provato il metodo splice con indice negativo, ho scoperto che se uso -1 va all'ultimo elemento dell'array, per -2 va al penultimo elemento e così via.
me_digvijay

+1. La lunghezza non ti dice quanti elementi ci sono o quante proprietà ci sono. È solo l'indice più alto + 1. Ad esempio var a = new Array(9), aha una lunghezza di 9 e non ha alcun elemento.
RobG

1
@DigvijayYadav - Sì, il Array.slice()metodo dovrebbe farlo. Il String.slice()metodo fa la stessa cosa.
nnnnnn

7

Notare che quando si utilizza un indice di posizione (o 0), i valori vengono inseriti all'interno della matrice:

var array = [];

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

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

Questo non è il caso quando aggiungi valori non di indice (non 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

I valori vengono inseriti nella matrice solo quando si gioca secondo le regole. Quando non lo fai, non vengono accettati. Tuttavia, sono accettati sull'oggetto Array stesso, come nel caso di qualsiasi cosa in JavaScript. Anche se ["Foo", "Bar"]sono gli unici valori nel nostro array, possiamo comunque accedere a "Fizzbuzz":

array[-1]; // "Fizzbuzz"

Ma nota ancora che questo non fa parte dei valori dell'array, poiché il suo "indice" non è valido. È stato invece aggiunto all'array come un altro membro. Potremmo accedere ad altri membri dell'array nello stesso modo:

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

Nota qui che stiamo accedendo al popmetodo sull'array, che ci informa che questo contiene codice nativo. Non stiamo accedendo a nessuno dei valori dell'array con una chiave "pop", ma piuttosto un membro sull'oggetto dell'array stesso. Possiamo ulteriormente confermarlo passando in rassegna i membri pubblici dell'oggetto:

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

Che sputa quanto segue:

 0 Foo
 1 Bar
-1 Fizzbuzz

Quindi, di nuovo, è il l' oggetto , ma non è nella matrice .

Domanda fantastica! Mi ha indotto a fare un doppio tentativo di sicuro.


1
+1 Anche la specifica afferma che le proprietà con un nome di proprietà numerico positivo sono chiamate elementi dell'array (e qualsiasi altra proprietà non lo è): es5.github.com/#x15.4 .
Felix Kling

Il risultato non è: ["Foo", "Bar"]ma ["Foo", "Bar", -1: "Fizzbuzz"]controlla il violino qui . Come hai scritto diventa una proprietà con il tasto -1 ma è decisamente visibile nell'output. Altrimenti la tua risposta è carina e completa. Se cambi, voterò di nuovo, il mio voto negativo era duro, ma non posso rimuoverlo; Tempo scaduto.
Appassire il

5

Se vuoi davvero includere indici negativi e altri indici nella lunghezza, crea tu stesso la funzionalità:

function getExtendedArray() {
    var a = [];

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

    return a;
}

Quindi ad esempio:

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

E se vuoi che la lunghezza includa solo gli indici / proprietà numerati puoi aggiungere un'istruzione if per la chiave - if(Number(key) != NaN) length++;Se vuoi filtrare quelli a virgola mobile aggiungi && key % 1 == 0alla condizione
Bonnev

4

L'array è solo un tipo speciale di oggetto in JS. C'è una sintassi speciale, che è buona, perché ci permette di lavorare con loro in modo efficace. Immagina quanto sarebbe noioso scrivere un array letterale in questo modo:

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

Ma sono comunque oggetti . Quindi se lo fai:

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

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

Quelle proprietà non intere verranno associate a un oggetto (quale array è), ma non sono accessibili da alcuni metodi nativi come Array.length.

Puoi presumere che il metodo nativo 'length' sia un getter che conta tutte le proprietà (e gli indici sono proprietà, mentre i valori sono i valori effettivi ) convertibili in numeri interi positivi o zero. Qualcosa di simile a questa pseudo-implementazione:

A causa delle specifiche , il lengthmetodo nativo - come getter ( exampleArray.length) - controllerà tutte le chiavi ' intero non negativo inferiore a 2 32 ', otterrà il più grande e restituirà il suo valore numerico + 1.

Come setter ( exampleArray.length = 42), creerà degli slot vuoti per gli indici 'mancanti' se la lunghezza data è maggiore del numero effettivo di chiavi intere non negative, o eliminerà tutte le chiavi intere non negative (e i loro valori) che non sono maggiori del data lunghezza.

La pseudo-implementazione:

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


3

Gli array in JavaScript sono in realtà oggetti. Sono semplicemente prototipati dal costruttore Array.

Gli indici di matrice sono in realtà chiavi in ​​una hashmap e tutte le chiavi vengono convertite in stringhe. Puoi creare qualsiasi chiave (es. "-1"), ma i metodi su Array sono fatti su misura per agire come un array. Quindi lengthnon è la dimensione dell'oggetto, piuttosto è solo garantito che sia maggiore dell'indice intero più grande. Allo stesso modo, la stampa arrelencherà solo i valori con chiavi intere> = 0.

Maggiori info qui.


Esattamente. oppure potremmo iniziare a chiederci perché array ['foo'] non è un indice valido: javascript ti permette di farlo, ma non significa che la definizione di ciò che un array (e dei suoi elementi) cambia da una lingua all'altra. funziona come previsto.
tw airball

Infatti gli "array" non esistono in JS. Ma gli "array associativi", noti anche come "oggetti", sono al centro di JS. Quindi non è stato un grande passo avanti creare un oggetto Array che imiti un normale array.
Tony R

1
C'è una differenza molto speciale tra array e oggetti semplici: la proprietà di auto-regolazione della lunghezza.
RobG

1

Secondo MDN:

Il valore della proprietà length è un numero intero con segno positivo e un valore inferiore a 2 alla potenza 32 (232)

In Javascript puoi impostare una proprietà su qualsiasi oggetto che crei.

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

Allo stesso modo, quando si imposta un indice negativo, lo memorizza come una proprietà sull'array piuttosto che come parte dell'indice.

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

Questo è il motivo per cui la lunghezza non lo riflette ma un ciclo for..in lo mostra.


-1

Quando si utilizzano indici non positivi o non numerici, l'array si comporta come un array associativo che ha coppie chiave-valore.

Per scorrere l'array puoi usare

for(var index in myArray) {
  document.write( index + " : " + myArray[index] + "<br />");
}
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.