Давайте посмотрим, есть ли разница между этими способами добавления элементов в массив в Javascript:

  • arr.push(item);
  • arr[arr.length] = item;

Если вы уже знаете разницу - продолжайте читать, возможно, вы найдете какие-то новые детали, которые вы пропустили раньше 🤓

Очевидно, что вам лучше использовать такой подход:

arr.push(item);

Основная причина в том, что встроенные методы очень оптимизированы и более предпочтительны для использования.

Но знаете ли вы, почему это лучше и в чем собственно разница?

ЗАБАВНЫЕ ФАКТЫ!

  • const a = [];или const a = new Array(); создает пустой массив, но выделяет 4 слота в памяти для будущего содержимого; (см. исходный код V8 Engine - https://chromium.googlesource.com/v8/v8.git/+/refs/heads/master/src/objects/js-array.h#109)
  • когда вы устанавливаете элемент вне длины массива (arr[arr.length] =item;), он обычно выделяет вдвое больше памяти и копирует массив в новый выделенный диапазон памяти.

Давайте узнаем некоторые подробности

Массивы в Javascript - это объекты, и очень простая структура объекта выглядит так (очень упрощенный пример, как описано здесь: https://stackoverflow.com/a/614255)

interface Object{
    put(key, value)
    get(key)
    private: map properties // any data structure (hashtable, map, set etc...)
}

Итак, Array будет выглядеть так:

interface Array:Object {
    override put(key, value)
    override get(key)
    private:
        map sparseStorage; 
        value[] flatStorage;
        value length; 
}

Где:

  • sparseStorage - это карта между целочисленными индексами и значениями
  • flatStorage - это собственный массив значений

Сравнение скорости

Я сделал 2 образца кода, чтобы проверить скорость.

ОБРАЗЕЦ №1

// arr.push(item)
const arr = [];
const iterationsTotal = 10000000;
console.time('arr.push(x)');
for(let i=0; i < iterationsTotal; i++){
    arr.push(i + 'test');
}
console.timeEnd('arr.push(x)');

ОБРАЗЕЦ №2

// arr[arr.length] = item
const arr = [];
const iterationsTotal = 10000000;
console.time('arr[arr.length] = x');
for(let i=0; i < iterationsTotal; i++){
    arr[arr.length] = i + 'test';
}
console.timeEnd('arr[arr.length] = x');

В большинстве случаев ОБРАЗЕЦ №1 на ~ 9% быстрее.

Чтобы лучше понять, почему это быстрее, я сделал ОБРАЗЕЦ №3:

// arr[arr.length + ""] = item
const arr = [];
const iterationsTotal = 10000000;
console.time('arr[arr.length + ""] = x');
for(let i=0; i < iterationsTotal; i++){
    arr[arr.length+''] = i + 'test';
}
console.timeEnd('arr[arr.length + ""] = x');

А ОБРАЗЕЦ № 3 примерно на 15% медленнее, чем ОБРАЗЕЦ № 1.

Объяснение!

Когда вы пытаетесь получить доступ к элементу массива по индексу, движок попытается преобразовать индекс в число и после этого проверит, находится ли индекс в допустимом диапазоне. В соответствии с исходным кодом V8 диапазон составляет от 0 до 2 ² -1.

Вы можете увидеть это здесь - https://chromium.googlesource.com/v8/v8.git/+/refs/heads/master/src/objects/js-array.h#131

Он был добавлен в V8 Simon Zünd на этом коммите - https://chromium-review.googlesource.com/c/v8/v8/+/1126922/

Между тем arr.push(item) не заботится о формате и значении индекса. Он просто берет свойство length и увеличивает его. Вот почему это быстрее;

Заключение

Я не говорю, что вы всегда использовали только array.push(), но вам просто нужно знать разницу.

У каждой проблемы есть собственное «хорошее» решение, и, зная разницу в подходах, вы можете написать более эффективный код для решения конкретной проблемы.

Удачи!

Фото в шапке - Дэнни Менесес из Pexels