Давайте посмотрим, есть ли разница между этими способами добавления элементов в массив в 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