A classificação das propriedades em Javascript está quebrada

15

Eu preciso percorrer um objeto JavaScript tratando-o como uma matriz com chaves personalizadas. Eu sei que isso não é totalmente suportado, já que as propriedades não têm ordem instrínseca, mas como eu sempre reorganizo as propriedades, achei essa abordagem simples e confiável ... até agora.

O problema ocorre quando as teclas são números ou cadeias que podem ser convertidas como números.

Quando executo esse código:

var test1 = {4294966222:"A",4294966333:"A",4294966111:"A"};
var test2 = {4294968222:"A",4294968333:"A",4294968111:"A"};
        
for (var k in test1) {console.log(k);}
console.log("---");
for (var k in test2) {console.log(k);}

a saída é:

4294966111
4294966222
4294966333
---
4294968222
4294968333
4294968111

Que significa:

  • (test1) se as chaves estiverem abaixo de 2 ^ 32 (4.294.967.296), elas serão reordenadas automaticamente, a menor primeira
  • (test2) se as chaves estiverem acima de 2 ^ 32, elas NÃO serão reordenadas.

A questão é: por que isso está acontecendo?

Como todos os navegadores que testei (Google Chrome 79.0, Mozilla Firefox 71.0, Microsoft Edge 44.18362, Internet Explorer 11.535) concordam com essa saída, deve haver alguma especificação oficial.

Atualizar

Testei muitos números antes de descobrir que era uma questão de limiar. Achei estranho que a sequência 2,3,1 se comporte de maneira diferente de três registros de data e hora ordenados da mesma maneira.

Carma
fonte
meu palpite é como o código de hash é calculado, mas não é uma resposta real para sua pergunta.
Mario Vernari
11
Não acho que seja quebrado no verdadeiro sentido coloquial da palavra, eles não garantem que os valores sejam iterados por ordem, uma vez que são executados arbitrariamente, como você pode conferir em developer.mozilla.org/en-US/docs/Web / JavaScript / Reference /… "Nota: for ... in não deve ser usado para iterar sobre uma matriz em que a ordem do índice é importante." Eles garantem apenas a iteração sobre todos os elementos da coleção. Algo como o forEach, de fato, leva o pedido em consideração, percorrendo itens em ordem crescente ecma-international.org/ecma-262/5.1/#sec-15.4.4.18
Mr.Toxy
Para o registro, você pode ver o problema diretamente registrando test1e test2. Eu acho que o "problema" é do cache de chaves na implementação da especificação V8.
Seblor
É mais do que abaixo de 2 ^ 32 que o nome da sua propriedade é ordenado por coincidência, semelhante às referências internas da propriedade. Você pode e não deve confiar na ordem das propriedades do objeto, pois, por definição, elas não são ordenadas e podem conter propriedades inerentes ao objeto. Sempre projete / mapeie seu objeto em uma matriz, classifique a matriz e faça uma repetição, se a ordem for importante.
user3154108
11
@ Mr.Toxy Isso ocorre porque essas propriedades 4294968333e 4294968111são maiores que 2 ** 32(o que é 4294967296). Portanto, eles não são indicações de matriz, portanto são iterados em ordem de criação de propriedade, em vez de em ordem numérica crescente - que é exatamente o que eles estão fazendo no violino, como esperado. (veja minha resposta)
CertainPerformance

Respostas:

4

Isso é esperado. De acordo com a especificação , o método que itera sobre as propriedades OrdinaryOwnPropertyKeys:

  1. Para cada chave de propriedade P de O que é um índice de matriz , em ordem crescente de índice numérico, faça

    uma. Adicione P como o último elemento das chaves.

  2. Para cada chave de propriedade P de O que é uma String, mas não é um índice de matriz, em ordem cronológica crescente de criação de propriedade, faça

    uma. Adicione P como o último elemento das chaves.

A ordem numérica crescente aplica-se apenas às propriedades que são indicações de matriz.

Então, o que é um "índice de matriz"? Procure ::

Um índice inteiro é uma chave de propriedade com valor de String que é uma String numérica canônica (consulte 7.1.21) e cujo valor numérico é +0 ou um número inteiro positivo ≤ 2 ^ 53 - 1. Um índice de matriz é um índice inteiro cujo número numérico o valor i está no intervalo +0 ≤ i <2 ^ 32 - 1.

Portanto, propriedades numéricas maiores que 2 ^ 32 não são indicações de matriz e, portanto, iteraram na ordem de criação da propriedade. No entanto, propriedades numéricas menores que 2^32 são indicativas de matriz e são iteradas em ordem numérica crescente.

Então, por exemplo:

1: O índice da matriz será iterado numericamente

10: O índice da matriz será iterado numericamente

4294968111: Maior que 2 ** 32, será iterado após a conclusão das indicações da matriz, na ordem de criação da propriedade

9999999999999: Maior que 2 ** 32, será iterado após a conclusão das indicações da matriz, na ordem de criação da propriedade

Além disso, lembre-se de que, contrariamente à crença popular, a ordem de iteração de propriedade também é garantida pela especificação, graças à proposta de iteração integrada, que é o estágio 4.

CertainPerformance
fonte
2

Isso tem a ver com a maneira como as chaves de um objeto são percorridas.

De acordo com as especificações ES6, deve ser:

9.1.12 [[OwnPropertyKeys]] ( )

When the [[OwnPropertyKeys]] internal method of O is called the following steps are taken:

    Let keys be a new empty List.
    For each own property key P of O that is an integer index, in ascending numeric index order
        Add P as the last element of keys.
    For each own property key P of O that is a String but is not an integer index, in property creation order
        Add P as the last element of keys.
    For each own property key P of O that is a Symbol, in property creation order
        Add P as the last element of keys.
    Return keys.

http://www.ecma-international.org/ecma-262/6.0/#sec-ordinary-object-internal-methods-and-internal-slots-ownpropertykeys

Isso significa que se o valor de uma chave permanecer o mesmo se convertido em um número de 53 bits não assinado e vice-versa, ele será tratado como um índice inteiro que é classificado em ordem numérica crescente.

Se isso falhar, é tratado como uma chave de cadeia, ordenada da maneira que foi adicionada ao objeto.

O problema aqui é que todos os principais navegadores ainda não seguem essa especificação e usam um índice de matriz, que é limitado a um número positivo de até 2 ^ 32-1. Portanto, qualquer coisa acima desse limite é realmente uma chave de cadeia.

obscurecer
fonte