Nombre de clés de tableaux et de & # 8220; numéros & # 8221; sont considérés de manière inattendue les mêmes

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

Question

Je joue avec les tableaux javascript et je me suis heurté à des incohérences. J'espère que quelqu'un pourra les expliquer pour moi.

Commençons par ceci:


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

Ce code n'a rien de spécial, mais je comprends qu'un tableau javascript est un objet. Par conséquent, les propriétés peuvent être ajoutées au tableau, la façon dont elles sont ajoutées à un tableau me semble incohérente.

Avant de continuer, laissez-moi vous expliquer comment les valeurs de chaîne doivent être converties en valeurs numériques en javascript.

  • Chaîne non vide - > Valeur numérique de chaîne ou NaN

  • Chaîne vide - > 0

Donc, comme un tableau javascript est un objet, ce qui suit est légal:


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

La sortie est inattendue.

La chaîne non vide "4" est convertie en sa valeur numérique lors de la définition de la propriété myArray ["4"], cela semble correct. Cependant, la chaîne vide " " n'est pas converti en sa valeur numérique, 0, il est traité comme une chaîne vide. La chaîne non vide " quelque chose " n'est pas converti en sa valeur numérique, NaN, il est traité comme une chaîne. Alors c'est quoi? l’instruction dans myArray [] est-elle numérique ou sous forme de chaîne?

Aussi, pourquoi les deux propriétés non numériques de myArray ne sont-elles pas incluses dans myArray.length et myArray.join ("", ")?

Était-ce utile?

La solution

Les clés d'un tableau JavaScript sont en fait des chaînes. Pour plus de détails et une implémentation d'un type de carte pour des clés arbitraires, consultez cette réponse .

Pour clarifier et ajouter à ce que Jason a posté: Les tableaux JavaScript sont des objets. Les objets ont des propriétés. Un nom de propriété est une valeur de chaîne. Par conséquent, les indices de tableau sont également convertis en chaînes avant que rien ne puisse se produire. Un nom de propriété P sera traité comme un index de tableau (c’est-à-dire que le tableau magique spécial sera appelé) si les restrictions suivantes sont conservées (ECMA-262, 15.4):

  

ToString (ToUint32 (P)) est égal à P et ToUint32 (P) n’est pas égal à 2 ^ 32 & # 8722; 1

Il est facile de vérifier que les index numériques convertis en chaînes (et non l'inverse):

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

De même, il est déconseillé de parcourir les entrées d'un tableau avec une boucle pour..dans - vous pourriez obtenir des résultats inattendus si quelqu'un manipulait certains prototypes (et ce n'est pas très rapide non plus). Utilisez le standard pour (var i = 0; i < array.length; ++ i) à la place.

Autres conseils

(éditer: ce qui suit n’est pas tout à fait correct)

Les clés d'un objet JavaScript sont en fait des chaînes. Un tableau Javascript possède lui-même des index numériques. Si vous stockez quelque chose avec un index pouvant être interprété comme un entier non négatif, il essaiera de le faire. Si vous stockez quelque chose avec un index qui n'est pas un entier non négatif (par exemple alphanumérique, négatif ou un nombre à virgule flottante avec une fraction), il échouera dans le magasin tableau-index et utilisera par défaut le Object. (qui est la classe de base de Array), qui convertit ensuite l'argument en chaîne et le stocke par index de chaîne - mais ces propriétés stockées ne sont pas vues par la classe Array et ne sont donc pas visibles par ses méthodes / propriétés ( longueur, jointure, coupe, épissure, poussée, pop, etc.).

edit: ce qui précède n’est pas tout à fait correct (comme le montre l’exemple foo / bar / baz de Christopher). Les indices de stockage réels selon la spécification ECMAscript sont, dans fait, des chaînes, mais s’il s’agit d’indices de tableau valides (entiers non négatifs), la méthode [[Put]] de l’objet Array, qui est spéciale, rend ces valeurs particulières visibles par le tableau "tableau-ish" ; méthodes.

Ceci est une réponse au message de PhiLo . Son repère est erroné car il utilise différents noms de propriété pour la version de l'objet: il aurait dû utiliser i également et non x .

Si cela est fait correctement, par exemple comme ceci:

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

Vous verrez que les temps seront très proches. Dans FF3.0.5, la version du tableau est même toujours plus lente (dans Opera, c'est l'inverse).

Les tableaux, comme tout le reste en JavaScript, sont des objets. Les objets ont été dotés de la notation par points pour alléger le fardeau des développeurs. En utilisant votre exemple

myArray["someThing"] = "someThing";

est identique à l'écriture

myArray.someThing = "someThing";

Dans ce cas, vous ajoutez une propriété à l'objet au lieu de l'ajouter au tableau. Il en va de même avec la chaîne vide, bien que vous ne puissiez pas utiliser la notation par points pour une chaîne vide ... étrange, hein?

Dans le cas de "4", il peut être forcé dans un entier et est donc utilisé comme index dans le tableau.

Je ne suis pas d'accord avec Christoph lorsqu'il déclare "les indices de tableau sont convertis en chaînes".

Tout d'abord, je pense que cela dépend de la mise en œuvre ... Je suppose que les (bons) développeurs vont optimiser l'accès aux tableaux, il existe des moyens intelligents de le faire.

En fait, j’ai fait un petit test et bien que ce soit aussi bon que la plupart des micro-points de repère (c’est-à-dire pas très fiable), c’est intéressant:

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

Sur IE6, je reçois: Avec un tableau: 1453 Avec un objet: 3547
Sur FF 3.0, je reçois: Avec un tableau: 83 Avec un objet: 226
Sur Safari 3.1, je reçois: Avec un tableau: 140 Avec un objet: 313
Sur Opera 9.26, pour une raison quelconque, je n’obtiens pas le résultat, mais si je réduis au dixième du nombre de boucles, j’obtiens: Avec un tableau: 47 Avec un objet: 516
En fait, je laisse Opera s’exécuter pendant que je tape ceci et j’obtiens finalement le résultat suivant: Avec un tableau: 281 Avec un objet: 166063 ...

Les tableaux sont donc optimisés! Qui est chanceux ...
La démonstration de Christoph ne m'a pas impressionné. Ma conclusion serait plutôt que les chaînes qui peuvent être interprétées comme des nombres soient traitées comme telles, ce qui va de pair avec la formule citée ...

Donc, mon interprétation de vos résultats est que le tableau se comporte comme un tableau rapide avec des indices numériques quand il est alimenté par ceux-ci (avec peut-être un comportement de tableau associatif sur des valeurs rares, c’est-à-dire quelques grands index isolés), mais en tant qu’objet , il a toujours le traitement normal des propriétés. Mais ces propriétés ne sont pas gérées dans la partie tableau, d’où le résultat obtenu avec join ().

[EDIT] J'ai ajouté quelques boucles suivant l'idée de Christoph.
Sur FF3, je reçois: avec array: 92 avec s array: 93 avec objet (i): 243 avec s objet: 194 avec i objet: 125 (les perfs varient entre les exécutions, mais sont à peu près cohérents).

Je ne suis pas très convaincu de cet entier - > chaîne - > aller-retour entier, même pas que l'ECMA demande cette séquence. La façon dont je l'ai lu est la suivante: sa propriété est une chaîne et peut être interprétée comme un entier, elle est alors traitée comme telle.

Bien sûr, le seul moyen sûr de savoir serait de regarder une implémentation ...

Je remarque avec intérêt que les objets simples obtenant une propriété entière ou une propriété pouvant être transformée en un entier sont en quelque sorte optimisés. Peut-être parce que beaucoup de programmeurs JS utilisaient des objets simples en tant que tableaux, les développeurs ont donc jugé intéressant d'optimiser ce cas.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top