𝗥𝗲𝘀𝗲𝗮𝗿𝗰𝗵 𝗔𝗻𝗱 𝗥𝗲𝘀𝘂𝗹𝘁𝘀
Для факта, тест производительности в jsperf и проверка некоторых вещей в консоли выполняются. Для исследования используется сайт irt.org . Ниже собрана коллекция всех этих источников вместе с примером функции внизу.
╔═══════════════╦══════╦═════════════════╦════════ ═══════╦═════════╦══════════╗
║ Метод ║Concat║slice & push.apply ║ push.apply x2 ║ ForLoop ║Spread ║
╠═══════════════╬══════╬═════════════════╬════════ ═══════╬═════════╬══════════╣
║ МОП / сек ║179 ║104 ║ 76 ║ 81 ║28 ║
╠═══════════════╬══════╬═════════════════╬════════ ═══════╬═════════╬══════════╣
║ разреженные массивы ║ДА! TheТолько 2 нарезанных ║ нет ║ Возможно 2 ║но ║
║ скудный ║ rayarray (1-й аргумент) ║ ║ ║ ║
╠═══════════════╬══════╬═════════════════╬════════ ═══════╬═════════╬══════════╣
║ Поддержка ║ MSIE 4 ║ MSIE 5.5 ║ MSIE 5.5 ║ MSIE 4 ║ Edge 12 ║
║ ( источник ) ║NNav 4║NNav 4,06 ║ ║ NNav 4,06 NNav 3 ║ MSIE NNav ║
╠═══════════════╬══════╬═════════════════╬════════ ═══════╬═════════╬══════════╣
║Массивоподобные действия║но ║Только толкаемые ║ ДА! ║ ДА! HaveЕсли есть ║
║like массив ║ ║array (2 - й Арг) ║ ║ ║iterator 1 ║
╚═══════════════╩══════╩═════════════════╩════════ ═══════╩═════════╩══════════╝
1 Если массивоподобный объект не имеет свойства Symbol.iterator , то попытка
распространять это бросит исключение.
2 Зависит от кода. Следующий пример кода «ДА» сохраняет разреженность.
function mergeCopyTogether(inputOne, inputTwo){
var oneLen = inputOne.length, twoLen = inputTwo.length;
var newArr = [], newLen = newArr.length = oneLen + twoLen;
for (var i=0, tmp=inputOne[0]; i !== oneLen; ++i) {
tmp = inputOne[i];
if (tmp !== undefined || inputOne.hasOwnProperty(i)) newArr[i] = tmp;
}
for (var two=0; i !== newLen; ++i, ++two) {
tmp = inputTwo[two];
if (tmp !== undefined || inputTwo.hasOwnProperty(two)) newArr[i] = tmp;
}
return newArr;
}
Как видно из вышесказанного, я бы сказал, что Concat почти всегда является способом обеспечения как производительности, так и способности сохранять редкость запасных массивов. Затем для лайков массива (таких как DOMNodeLists document.body.children
) я бы рекомендовал использовать цикл for, поскольку он является вторым по производительности и единственным другим методом, который сохраняет разреженные массивы. Ниже мы быстро рассмотрим, что имеется в виду под разреженными массивами и подобными массивами, чтобы устранить путаницу.
𝗧𝗵𝗲 𝗙𝘂𝘁𝘂𝗿𝗲
Поначалу некоторые люди могут подумать, что это случайность, и что производители браузеров в конечном итоге доберутся до оптимизации Array.prototype.push, чтобы быть достаточно быстрыми, чтобы превзойти Array.prototype.concat. НЕПРАВИЛЬНО! Array.prototype.concat всегда будет быстрее (в принципе, по крайней мере), потому что это просто копирование и вставка данных. Ниже приведена упрощенная наглядная диаграмма того, как может выглядеть реализация 32-битного массива (обратите внимание, что реальные реализации намного сложнее)
Байт ║ Данные здесь
═════╬═══════════
0x00 ║ int nonNumericPropertiesLength = 0x00000000
0x01 ║ Там же
0x02 ║ Там же
0x03 ║ Там же
0x00 ║ int length = 0x00000001
0x01 ║ Там же
0x02 ║ Там же
0x03 ║ Там же
0x00 ║ int valueIndex = 0x00000000
0x01 ║ Там же
0x02 ║ Там же
0x03 ║ Там же
0x00 value int valueType = JS_PRIMITIVE_NUMBER
0x01 ║ Там же
0x02 ║ Там же
0x03 ║ Там же
0x00 ║ uintptr_t valuePointer = 0x38d9eb60 (или там, где он находится в памяти)
0x01 ║ Там же
0x02 ║ Там же
0x03 ║ Там же
Как видно выше, все, что вам нужно сделать, чтобы скопировать что-то подобное, почти так же просто, как копировать его байт за байтом. С Array.prototype.push.apply это намного больше, чем просто копирование и вставка данных. «.Apply» должен проверить каждый индекс в массиве и преобразовать его в набор аргументов, прежде чем передать его в Array.prototype.push. Затем Array.prototype.push должен дополнительно выделять больше памяти каждый раз, и (для некоторых реализаций браузера) может даже пересчитывать некоторые данные поиска позиции для разреженности.
Альтернативный способ думать об этом - это. Исходный массив один - это большая стопка бумаг, сшитых вместе. Массив источника два - это еще одна большая стопка бумаг. Будет ли это быстрее для вас
- Пойдите в магазин, купите достаточно бумаги, необходимой для копии каждого исходного массива. Затем пропустите каждую пачку бумаги исходного массива через копировальный аппарат и скрепите получающиеся две копии вместе.
- Пойдите в магазин, купите достаточно бумаги для одной копии первого исходного массива. Затем вручную скопируйте исходный массив на новую бумагу, чтобы заполнить все пустые редкие пятна. Затем вернитесь в магазин и купите достаточно бумаги для второго источника. Затем просмотрите второй исходный массив и скопируйте его, не оставляя пробелов в копии. Затем скрепите все скопированные документы вместе.
В приведенной выше аналогии вариант № 1 представляет Array.prototype.concat, а № 2 представляет Array.prototype.push.apply. Давайте проверим это с помощью аналогичного JSperf, отличающегося только тем, что этот тестирует методы над разреженными, а не сплошными массивами. Это можно найти прямо здесь .
Поэтому я ограничусь тем, что будущее производительности для этого конкретного варианта использования лежит не в Array.prototype.push, а скорее в Array.prototype.concat.
𝗖𝗹𝗮𝗿𝗶𝗳𝗶𝗰𝗮𝘁𝗶𝗼𝗻𝘀
𝗦𝗽𝗮𝗿𝗲 𝗔𝗿𝗿𝗮𝘆𝘀
Когда некоторые члены массива просто отсутствуют. Например:
// This is just as an example. In actual code,
// do not mix different types like this.
var mySparseArray = [];
mySparseArray[0] = "foo";
mySparseArray[10] = undefined;
mySparseArray[11] = {};
mySparseArray[12] = 10;
mySparseArray[17] = "bar";
console.log("Length: ", mySparseArray.length);
console.log("0 in it: ", 0 in mySparseArray);
console.log("arr[0]: ", mySparseArray[0]);
console.log("10 in it: ", 10 in mySparseArray);
console.log("arr[10] ", mySparseArray[10]);
console.log("20 in it: ", 20 in mySparseArray);
console.log("arr[20]: ", mySparseArray[20]);
Кроме того, javascript позволяет легко инициализировать запасные массивы.
var mySparseArray = ["foo",,,,,,,,,,undefined,{},10,,,,,"bar"];
𝗔𝗿𝗿𝗮𝘆-𝗟𝗶𝗸𝗲𝘀
Подобный массиву объект - это объект, который имеет хотя бы length
свойство, но не был инициализирован с помощью new Array
или[]
; Например, нижеприведенные объекты классифицируются как массивы.
{0: "foo", 1: "bar", длина: 2}
document.body.children
новый Uint8Array (3)
- Это похоже на массив, потому что хотя это (n) (типизированный) массив, приведение его к массиву изменяет конструктор.
(function () {возвращать аргументы}) ()
Наблюдайте, что происходит, используя метод, который преобразует лайки массивов в массивы, такие как слайс.
var slice = Array.prototype.slice;
// For arrays:
console.log(slice.call(["not an array-like, rather a real array"]));
// For array-likes:
console.log(slice.call({0: "foo", 1: "bar", length:2}));
console.log(slice.call(document.body.children));
console.log(slice.call(new Uint8Array(3)));
console.log(slice.call( function(){return arguments}() ));
- ПРИМЕЧАНИЕ. Это плохая практика - вызывать slice для аргументов функции из-за производительности.
Наблюдайте за тем, что происходит, используя метод, который не преобразует подобные массивы в массивы, такие как concat.
var empty = [];
// For arrays:
console.log(empty.concat(["not an array-like, rather a real array"]));
// For array-likes:
console.log(empty.concat({0: "foo", 1: "bar", length:2}));
console.log(empty.concat(document.body.children));
console.log(empty.concat(new Uint8Array(3)));
console.log(empty.concat( function(){return arguments}() ));