Сортировка свойств в Javascript нарушена

15

Мне нужно перебрать объект JavaScript, рассматривая его как массив с пользовательскими ключами. Я знаю, что это не полностью поддерживается, поскольку свойства не имеют собственного порядка, но так как я всегда изменяю порядок свойств, я нашел этот подход простым и надежным ... до сих пор.

Проблема возникает, когда ключи представляют собой числа или строки, которые могут быть преобразованы в числа.

Когда я запускаю этот код:

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

выход:

4294966111
4294966222
4294966333
---
4294968222
4294968333
4294968111

Что значит:

  • (test1), если ключи меньше 2 ^ 32 (4 294 967 296), они автоматически переупорядочиваются, самые маленькие сначала
  • (test2) если ключи выше 2 ^ 32, они НЕ переупорядочиваются.

Вопрос в том, почему это происходит?

Поскольку все протестированные мной браузеры (Google Chrome 79.0, Mozilla Firefox 71.0, Microsoft Edge 44.18362, Internet Explorer 11.535) согласны с этим выводом, должна быть какая-то официальная спецификация.

Обновить

Я проверил много цифр, прежде чем выяснил, что это пороговый уровень. Мне показалось странным, что последовательность 2,3,1 ведет себя не так, как три метки времени, упорядоченные одинаково.

Карма
источник
я думаю, как вычисляется хэш-код, но это не реальный ответ на ваш вопрос.
Марио Вернари
1
Я не думаю, что он сломан в реальном разговорном смысле слова, они не гарантируют, что значения будут повторяться по порядку, так как он работает произвольно, так как вы можете проверить developer.mozilla.org/en-US/docs/Web / JavaScript / Reference /… «Примечание: for ... in не должен использоваться для итерации по массиву, где важен порядок индекса». Они гарантируют итерацию только для каждого элемента в коллекции. Что-то вроде forEach действительно учитывает порядок, пересекая элементы в порядке возрастания ecma-international.org/ecma-262/5.1/#sec-15.4.4.18
Mr.Toxy
Для записи, вы можете увидеть проблему напрямую, войдя в систему test1и test2. Я думаю, что «проблема» связана с ключевым кешированием в реализации спецификации V8.
Seblor
Более того, ниже 2 ^ 32 ваше имя свойства упорядочено по совпадению, аналогично внутренним ссылкам на свойства. Вы можете и не должны полагаться на порядок свойств объекта, поскольку по определению они не упорядочены и могут содержать свойства, присущие объекту. Всегда приводите / отображайте ваш объект в массив, сортируйте массив, а затем просматривайте его, если важен порядок.
user3154108
1
@ Mr.Toxy Это потому, что эти свойства 4294968333и 4294968111больше, чем 2 ** 32(что есть 4294967296). Таким образом, они не являются указателями на массивы, поэтому они повторяются в порядке создания свойств, а не в порядке возрастания чисел, что, как и ожидалось, является именно тем, что они делают в скрипте. (см. мой ответ)
CertainPerformance

Ответы:

4

Это ожидается. В соответствии со спецификацией , метод, который перебирает свойства OrdinaryOwnPropertyKeys, делает:

  1. Для каждого собственного ключа свойства P of O, который является индексом массива , в порядке возрастания числового индекса выполните

    а. Добавьте P как последний элемент ключей.

  2. Для каждого собственного ключа свойства P of O, который является строкой, но не является индексом массива, в восходящем хронологическом порядке создания свойства выполните

    а. Добавьте P как последний элемент ключей.

Восходящий числовой порядок применяется только для свойств, которые являются указателями массива.

Итак, что такое «индекс массива»? Посмотри :

Целочисленный индекс - это ключ свойства со строковым значением, представляющий собой каноническую числовую строку (см. 7.1.21), числовое значение которой равно +0 или положительное целое число ≤ 2 ^ 53 - 1. Индекс массива - это целочисленный индекс, числовой значение i находится в диапазоне +0 ≤ i <2 ^ 32 - 1.

Таким образом, числовые свойства, которые больше чем 2 ^ 32, не являются указателями массива, и поэтому повторяются в порядке создания свойств. Тем не менее, числовые свойства, которые меньше , чем 2^32 в indicies массива, и итерации в восходящем числовом порядке.

Так, например:

1: Индекс массива, будет повторяться численно

10: Индекс массива, будет повторяться численно

4294968111: Больше 2 ** 32, будет повторяться после завершения индикации массива в порядке создания свойства

9999999999999: Больше 2 ** 32, будет повторяться после завершения индикации массива в порядке создания свойства

Кроме того , имейте в виду , что, вопреки распространенному мнению, порядок свойств итерация будет гарантировано спецификацией , а также, благодаря предложению о-в итерация , которая является 4 -й стадии.

CertainPerformance
источник
2

Это связано с тем, как они проходят ключи объекта.

Согласно спецификации ES6 это должно быть:

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

Это означает, что если значение ключа остается неизменным, если оно преобразовано в 53-разрядное число без знака и обратно, оно обрабатывается как целочисленный индекс, который сортируется в порядке возрастания чисел .

Если это не удается, он обрабатывается как строковый ключ, который упорядочен так, как он был добавлен к объекту.

Подвох в том, что все основные браузеры еще не следуют этой спецификации и вместо этого используют индекс массива, который ограничен положительным числом до 2 ^ 32-1. Так что все, что выше этого предела, на самом деле является строковым ключом.

затенить
источник