número de chaves de arrays e “número” de forma inesperada são considerados o mesmo

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

Pergunta

Eu tenho jogado com matrizes de JavaScript e eu ter executado em, o que eu sinto, algumas inconsistências, espero que alguém possa explicá-las para mim.

Vamos começar com este:


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

Não há nada de especial sobre este código, mas eu entendo que uma matriz de javascript é um objeto, de modo properities podem ser adicionados ao conjunto, a forma como estes properities são adicionados a uma matriz parece inconsistente para mim.

Antes de continuar, deixe-me notar como valores de cadeia devem ser convertidas para valores numéricos em javascript.

  • string não vazio -> Valor numérico de corda ou NaN

  • string vazio -> 0

Assim, uma vez que uma matriz de javascript é um objeto a seguir é 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

A saída é inesperado.

A cadeia não vazia "4" é convertido em seu valor numérico ao definir o myArray propriedade [ "4"], isso parece certo. No entanto, a cadeia vazia "" não é convertido no seu valor numérico, 0, ele é tratado como uma cadeia vazia. Também a não string "algo" vazio não é convertido em seu valor numérico, NaN, ela é tratada como uma string. Então, qual é? é a declaração dentro myArray [] no contexto numérico ou string?

Além disso, por que os dois, não numérico, properities de myArray não incluídos no myArray.length e myArray.join ( "")?

Foi útil?

Solução

As chaves de uma matriz JavaScript são realmente cordas. Para obter detalhes e uma implementação de um tipo de mapa para chaves arbitrárias, verifique esta resposta .


Para esclarecer e acrescentar ao que Jason postou: matrizes de JavaScript são objetos. Os objetos têm propriedades. Um nome de propriedade é um valor string. Portanto, os índices da matriz são convertidos em cadeias, bem antes de mais nada pode acontecer. Um nome de propriedade P vai ser tratado como um índice de matriz (ou seja, a matriz de mágica especial irá ser invocada), se se mantém o seguinte (ECMA-262, 15,4):

ToString (ToUint32 (P)) é igual a P e ToUint32 (P) não é igual a 2 ^ 32-1

Isso índices numéricos serão convertidos para strings (e não o contrário) pode ser facilmente verificada:

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

Além disso, a sua má prática para interagir sobre as entradas de uma matriz com um loop for..in - que você pode obter resultados inesperados se alguém mexeu com alguns protótipos (e não é muito rápido, qualquer um). Use o for(var i= 0; i < array.length; ++i) padrão em vez.

Outras dicas

(edit: o seguinte não é muito justo)

As chaves de um JavaScript objeto são realmente cordas. A Javascript array , por si só tem índices numéricos. Se você armazenar algo com um índice que pode ser interpretado como um inteiro não negativo, ele vai tentar fazê-lo. Se você armazenar algo com um índice que não é um inteiro não negativo (por exemplo, a sua alfanumérico, negativo ou um número de ponto flutuante com um pedaço fracionário), ele irá falhar na loja matriz índice e padrão para o Objeto (que é classe base de array) loja, que, em seguida, converte o argumento para uma cadeia e armazena pelo índice string - mas essas propriedades armazenados não são vistos pela classe array e, portanto, não são visíveis para os seus métodos / propriedades ( comprimento, junte-se, fatia, emenda, empurrar, pop, etc).

edit: o acima não é muito justo (como exemplo foo / bar / baz mostras de Christopher). Os índices de armazenamento real de acordo com o ECMAscript especificação são, em fato, cordas, mas se eles são índices de matriz válidas (inteiros não negativos), em seguida, método [[Put]] do objeto array, que é especial, faz com que aqueles valores particulares visíveis aos métodos "array-ish" da matriz.

Arrays, como tudo em JavaScript, são objetos. Objetos foram abençoados com o dot notação para aliviar o fardo sobre os desenvolvedores. Usando o seu exemplo

myArray["someThing"] = "someThing";

é o mesmo que escrever

myArray.someThing = "someThing";

Neste caso, você está adicionando uma propriedade para o objeto em vez de adicionar-lo para a matriz. O mesmo acontece com a cadeia vazia, embora você não pode usar o dot notação para uma cadeia vazia ... estranho, hein?

No caso de "4", que pode ser coagido a um número inteiro, e, portanto, é usado como um índice para a matriz.

Não concordo com Christoph quando afirma "índices da matriz são convertidos em cadeias".

Em primeiro lugar, eu acho que é dependente de implementação ... Suponho (Bom) implementadores irá otimizar acesso à matriz, existem algumas maneiras inteligentes de fazê-lo.

Na verdade, eu fiz um pequeno teste, e embora seja tão bom quanto a maioria dos micro-benchmarks, é interessante (ou seja, não super-confiáveis.):

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

No IE6, eu recebo: Com matriz: 1453 Com objeto: 3547
No FF 3.0, eu recebo: Com matriz: 83 Com objeto: 226
No Safari 3.1, recebo: Com matriz: 140 Com objeto: 313
No Opera 9.26, por algum motivo eu não obter o resultado, mas se eu reduzir ao décimo do número de loops, eu recebo: Com matriz: 47 Com objeto: 516
Na verdade, eu deixo Opera run enquanto digito isso, e finalmente o resultado: Com matriz: 281 Com objeto: 166063 ...

Assim, matrizes são otimizados! Que é sorte ...
demonstração de Christoph não me impressionar. Minha conclusão seria mais que as cordas que podem ser interpretadas como números são tratados como tal, que vão junto com a fórmula citada ...

Assim, a minha interpretação de seus resultados é que a matriz está se comportando como uma matriz rápida com índices numéricos quando a alimentação com estes (talvez com um comportamento de matriz associativa em valores esparsos, ie. Alguns índices grandes isoladas), mas como um objeto , ele ainda tem a manipulação normal das propriedades. Mas essas propriedades não são tratados na parte matriz, portanto, o resultado que você tem com join ().

[EDIT] Eu adicionei algumas voltas, seguindo a idéia de Christoph.
Em FF3, fico: Com matriz: matriz 92 com S: 93 Com o objecto (i): 243 Com s objecto: 194 com i objeto: 125 (perfs variar entre as execuções, mas são aproximadamente consistente).

Eu não sou super-convencido disso inteiro -> string -> número inteiro de ida e volta, nem mesmo que ECMA solicita essa sequência. A maneira que eu lê-lo é:. É a propriedade é uma cadeia e pode ser interpretado como um número inteiro, então ele é tratado como tal

É claro que a única maneira segura de saber seria a olhar para uma implementação ...

Eu observo com interesse que objetos simples recebendo uma propriedade inteiro ou uma propriedade que pode ser transformada em um inteiro estão de alguma forma otimizada. Talvez porque muitos programadores JS usado objetos simples como matrizes, assim implementadores julgaram interessante para otimizar neste caso.

scroll top