Matrice numero chiavi e & # 8220; numero & # 8221; sono inaspettatamente considerati uguali

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

Domanda

Ho giocato con array javascript e mi sono imbattuto in, ciò che sento, sono alcune incongruenze, spero che qualcuno possa spiegarle per me.

Iniziamo con questo:


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

Non c'è nulla di speciale in questo codice, ma capisco che un array javascript sia un oggetto, quindi le proprietà possono essere aggiunte all'array, il modo in cui queste proprietà vengono aggiunte a un array mi sembra incoerente.

Prima di continuare, fammi notare come i valori di stringa devono essere convertiti in valori numerici in javascript.

  • Stringa non vuota - > Valore numerico di stringa o NaN

  • stringa vuota - > 0

Quindi, poiché un array javascript è un oggetto, è legale quanto segue:


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

L'output è imprevisto.

La stringa non vuota " 4 " viene convertito nel suo valore numerico quando si imposta la proprietà myArray [" 4 "], questo sembra giusto. Tuttavia, la stringa vuota " " non viene convertito nel suo valore numerico, 0, viene trattato come una stringa vuota. Anche la stringa non vuota " qualcosa " non viene convertito nel suo valore numerico, NaN, viene trattato come una stringa. Quindi che cos'è? è l'istruzione dentro myArray [] nel contesto numerico o stringa?

Inoltre, perché le due proprietà non numeriche di myArray non sono incluse in myArray.length e myArray.join (" ;, ")?

È stato utile?

Soluzione

Le chiavi di un array JavaScript sono in realtà stringhe. Per dettagli e un'implementazione di un tipo di mappa per chiavi arbitrarie, controlla questa risposta .


Per chiarire e aggiungere ciò che Jason ha pubblicato: le matrici JavaScript sono oggetti. Gli oggetti hanno proprietà. Un nome di proprietà è un valore di stringa. Pertanto, anche gli indici di array vengono convertiti in stringhe prima che accada qualcosa di più. Un nome di proprietà P verrà trattato come un indice di array (ovvero verrà invocato lo speciale array-magic) se vale quanto segue (ECMA-262, 15.4):

  

ToString (ToUint32 (P)) è uguale a P e ToUint32 (P) non è uguale a 2 ^ 32 & # 8722; 1

Che gli indici numerici verranno convertiti in stringhe (e non viceversa) possono essere facilmente verificati:

var array = [];
array[1] = 'foo';
array['1'] = 'bar';
array['+1'] = 'baz';
document.writeln(array[1]); // outputs bar

Inoltre, è una cattiva pratica scorrere le voci di un array con un ciclo for..in - si potrebbero ottenere risultati inaspettati se qualcuno ha incasinato alcuni prototipi (e non è nemmeno molto veloce). Utilizzare invece lo standard per (var i = 0; i < array.length; ++ i) .

Altri suggerimenti

(modifica: il seguente non è del tutto corretto)

Le chiavi di un oggetto JavaScript sono in realtà stringhe. Un array Javascript da solo ha indici numerici. Se memorizzi qualcosa con un indice che può essere interpretato come un numero intero non negativo, proverà a farlo. Se si memorizza qualcosa con un indice che non è un numero intero non negativo (ad es. È un numero alfanumerico, negativo o in virgola mobile con un pezzo frazionario), non riuscirà nel negozio indice-array e verrà automaticamente impostato sull'oggetto (che è la classe base di Array), che converte quindi l'argomento in una stringa e memorizza in base all'indice di stringa, ma queste proprietà memorizzate non sono viste dalla classe Array e quindi non sono visibili ai suoi metodi / proprietà ( lunghezza, join, slice, splice, push, pop, ecc.)

modifica: quanto sopra non è del tutto corretto (come mostra l'esempio foo / bar / baz di Christopher). Gli indici di archiviazione effettivi secondo le Specifiche ECMAscript sono, in fatto, stringhe, ma se sono indici di array validi (numeri interi non negativi), il metodo [[Put]] dell'oggetto Array, che è speciale, rende quei valori particolari visibili all'array " array-ish " ; metodi.

Questa è una risposta a Post di PhiLo . Il suo benchmark è errato perché usa nomi di proprietà diversi per la versione dell'oggetto: avrebbe dovuto usare anche i e non x .

Se eseguito correttamente, ad esempio in questo modo:

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));

Vedrai che i tempi saranno molto vicini. In FF3.0.5, la versione dell'array è persino più lenta (in Opera, è il contrario).

Le matrici, come tutto il resto in JavaScript, sono oggetti. Gli oggetti sono stati benedetti con la dot notation per alleviare l'onere per gli sviluppatori. Usando il tuo esempio

myArray["someThing"] = "someThing";

è uguale alla scrittura

myArray.someThing = "someThing";

In questo caso, si sta aggiungendo una proprietà sull'oggetto anziché aggiungerla all'array. Lo stesso vale con la stringa vuota, sebbene non sia possibile utilizzare la notazione con punti per una stringa vuota ... strano, eh?

Nel caso di "4", può essere forzato in un numero intero e quindi viene utilizzato come indice nell'array.

Non sono d'accordo con Christoph quando afferma che "gli indici di array vengono convertiti in stringhe".

In primo luogo, penso che dipenda dall'implementazione ... Suppongo che i (buoni) implementatori ottimizzeranno l'accesso all'array, ci sono alcuni modi intelligenti per farlo.

In realtà, ho fatto un piccolo test, e sebbene sia buono come la maggior parte dei micro-benchmark (cioè non super affidabile), è interessante:

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

Su IE6, ottengo: Con array: 1453 Con oggetto: 3547
Su FF 3.0, ottengo: Con array: 83 Con oggetto: 226
Su Safari 3.1 ottengo: Con array: 140 Con oggetto: 313
Su Opera 9.26, per qualche motivo non ottengo il risultato, ma se riduco al decimo numero di loop, ottengo: Con array: 47 Con oggetto: 516
In realtà, ho lasciato funzionare Opera mentre scrivevo questo, e finalmente ho ottenuto il risultato: Con array: 281 Con oggetto: 166063 ...

Quindi gli array sono ottimizzati! Che è una fortuna ...
La dimostrazione di Christoph non mi ha impressionato. La mia conclusione sarebbe più che le stringhe che possono essere interpretate come i numeri vengono trattate come tali, che vanno di pari passo con la formula citata ...

Quindi la mia interpretazione dei tuoi risultati è che l'array si comporta come un array veloce con indici numerici quando si nutre di questi (con forse un comportamento di array associativo su valori sparsi, cioè alcuni grandi indici isolati), ma come un oggetto , ha ancora la normale gestione delle proprietà. Ma queste proprietà non sono gestite nella parte dell'array, quindi il risultato ottenuto con join ().

[EDIT] Ho aggiunto alcuni loop, seguendo l'idea di Christoph.
Su FF3, ottengo: Con array: 92 Con array: 93 Con oggetto (i): 243 Con oggetto s: 194 Con oggetto i: 125 (i perf variano tra le esecuzioni, ma sono approssimativamente coerenti).

Non sono super convinto di questo numero intero - > stringa - > andata e ritorno intera, nemmeno che l'ECMA richiede questa sequenza. Il modo in cui lo leggo è: è la proprietà è una stringa e può essere interpretata come numero intero, quindi viene trattata come tale.

Ovviamente, l'unico modo sicuro per sapere sarebbe guardare un'implementazione ...

Noto con interesse che gli oggetti semplici che ottengono una proprietà intera o una proprietà che può essere trasformata in un numero intero sono in qualche modo ottimizzati. Forse perché molti programmatori JS hanno usato oggetti semplici come array, quindi gli implementatori hanno ritenuto interessante ottimizzare questo caso.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top