В игру вступают всевозможные факторы, в большинстве реализаций JS используется плоский массив, который преобразуется в разреженное хранилище, если в дальнейшем это становится необходимым.
По сути, решение стать разреженным является эвристическим, основанным на том, какие элементы устанавливаются, и сколько места будет потрачено впустую, чтобы остаться плоским.
В вашем случае вы устанавливаете последний элемент первым, что означает, что механизм JS увидит массив, который должен иметь длину n
, но только один элемент. Если n
достаточно велико, это немедленно сделает массив разреженным массивом - в большинстве движков это означает, что все последующие вставки будут иметь дело с медленным разреженным массивом.
Вы должны добавить дополнительный тест, в котором вы заполняете массив от индекса 0 до индекса n-1 - он должен быть намного, намного быстрее.
В ответ на @Christoph и из-за желания прокрастинировать, вот описание того, как массивы (как правило) реализуются в JS - специфика варьируется от движка JS к движку JS, но общий принцип тот же.
Все JS Object
(а не строки, числа, true, false, undefined
или null
) наследуются от базового типа объекта - точная реализация может быть разной, это может быть наследование C ++ или вручную в C (есть преимущества в любом случае) - базовый тип Object определяет методы доступа к свойствам по умолчанию, например.
interface Object {
put(propertyName, value)
get(propertyName)
private:
map properties; // a map (tree, hash table, whatever) from propertyName to value
}
Этот тип Object обрабатывает всю стандартную логику доступа к свойствам, цепочку прототипов и т. Д.
Тогда реализация Array становится
interface Array : Object {
override put(propertyName, value)
override get(propertyName)
private:
map sparseStorage; // a map between integer indices and values
value[] flatStorage; // basically a native array of values with a 1:1
// correspondance between JS index and storage index
value length; // The `length` of the js array
}
Теперь, когда вы создаете массив в JS, движок создает что-то похожее на вышеуказанную структуру данных. Когда вы вставляете объект в экземпляр Array, метод put массива проверяет, является ли имя свойства целым числом (или его можно преобразовать в целое число, например, «121», «2341» и т. Д.) В диапазоне от 0 до 2 ^ 32. -1 (или, возможно, 2 ^ 31-1, я точно забыл). Если это не так, то метод put перенаправляется в реализацию базового объекта, и стандартная логика [[Put]] выполняется. В противном случае значение помещается в собственное хранилище массива, если данные достаточно компактны, то движок будет использовать хранилище плоского массива, в этом случае вставка (и извлечение) является просто стандартной операцией индексации массива, в противном случае движок преобразует массив Разрежьте хранилище и поместите / получите карту, чтобы перейти от propertyName к значению location.
Честно говоря, я не уверен, что какой-либо движок JS в настоящее время конвертирует из разреженного в плоское хранилище после того, как происходит это преобразование.
Во всяком случае, это достаточно общий обзор того, что происходит, и опускается ряд более неприятных деталей, но это общая схема реализации. Специфика того, как распределяется дополнительное хранилище, и как отправляются / извлекаются, отличается от движка к движку - но это самое ясное, что я могу действительно описать дизайн / реализацию.
Незначительная точка сложения, в то время как спецификация ES ссылается на propertyName
как на строку, движки JS, как правило, специализируются также на целочисленных поисках, поэтому someObject[someInteger]
не будет преобразовывать целое число в строку, если вы смотрите на объект, который имеет целочисленные свойства, например. Типы Array, String и DOM (NodeList
s и т. Д.).