𝗥𝗲𝘀𝗲𝗮𝗿𝗰𝗵 𝗔𝗻𝗱 𝗥𝗲𝘀𝘂𝗹𝘁𝘀
W rzeczywistości przeprowadzany jest test wydajności w jsperf i sprawdzanie niektórych rzeczy w konsoli. Do badań wykorzystano stronę irt.org . Poniżej znajduje się zbiór wszystkich tych źródeł razem oraz przykładowa funkcja na dole.
╔═══════════════╦══════╦═════════════════╦════════ ═══════╦═════════╦══════════╗
║ Metoda onKoccat║slice i push.apply ║ push.apply x2 ║ ForLoop ║Spread ║
╠═══════════════╬══════╬═════════════════╬════════ ═══════╬═════════╬══════════╣
║ mOps / s ║179 ║104 ║ 76 ║ 81 ║28 ║
╠═══════════════╬══════╬═════════════════╬════════ ═══════╬═════════╬══════════╣
║ Rzadkie tablice ║ TAK! N Tylko pokrojone ║ nie ║ Może 2 ║ nie ║
║ przechowywane rzadkie ║ ║ tablica (1. arg) ║ ║ ║ ║
╠═══════════════╬══════╬═════════════════╬════════ ═══════╬═════════╬══════════╣
║ Wsparcie ║ MSIE 4 ║ MSIE 5.5 ║ MSIE 5.5 ║ MSIE 4 ║ Krawędź 12 ║
║ ( źródło ) ║NNav 4║NNav 4.06 ║ NNav 4.06 ║ NNav 3 ║ MSIE NNav ║
╠═══════════════╬══════╬═════════════════╬════════ ═══════╬═════════╬══════════╣
║ Akty podobne do tablic║nie ║ Tylko popychane ║ TAK! ║ TAK! F Jeśli masz ║
║ jak tablica ║ ║ tablica (2. arg) ║ ║ ║iterator 1 ║
╚═══════════════╩══════╩═════════════════╩════════ ═══════╩═════════╩══════════╝
1 Jeśli obiekt podobny do tablicy nie ma właściwości Symbol.iterator , spróbuj
rozprzestrzenienie spowoduje wyjątek.
2 Zależy od kodu. Poniższy przykładowy kod „TAK” zachowuje rzadkość.
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;
}
Jak widać powyżej, argumentowałbym, że Concat prawie zawsze jest drogą do osiągnięcia zarówno wydajności, jak i zdolności do zachowania rzadkości zapasowych macierzy. Następnie, dla podobnych do tablicy (takich jak DOMNodeLists, jak document.body.children
), zaleciłbym użycie pętli for, ponieważ jest ona zarówno 2. najbardziej wydajną, jak i jedyną inną metodą, która zachowuje rzadkie tablice. Poniżej szybko przejrzymy, co oznaczają rzadkie tablice i tablice-lubi wyjaśnić zamieszanie.
𝗧𝗵𝗲 𝗙𝘂𝘁𝘂𝗿𝗲
Na początku niektórzy ludzie mogą myśleć, że jest to przypadek i że producenci przeglądarek w końcu zaczną optymalizować Array.prototype.push, aby być wystarczająco szybkim, aby pokonać Array.prototype.concat. ŹLE! Array.prototype.concat zawsze będzie szybszy (przynajmniej w zasadzie), ponieważ jest to zwykłe kopiowanie i wklejanie danych. Poniżej znajduje się uproszczony perswaado-wizualny schemat tego, jak może wyglądać implementacja tablicy 32-bitowej (pamiętaj, że prawdziwe implementacje są dużo bardziej skomplikowane)
Bajt ║ Dane tutaj
═════╬═══════════
0x00 ║ int nonNumericPropertiesLength = 0x00000000
0x01 ║ ibid
0x02 ║ ibid
0x03 ║ ibid
0x00 ║ int długość = 0x00000001
0x01 ║ ibid
0x02 ║ ibid
0x03 ║ ibid
0x00 ║ int wartośćIndeks = 0x00000000
0x01 ║ ibid
0x02 ║ ibid
0x03 ║ ibid
0x00 ║ int valueType = JS_PRIMITIVE_NUMBER
0x01 ║ ibid
0x02 ║ ibid
0x03 ║ ibid
0x00 ║ wartość uintptr_t Wskaźnik = 0x38d9eb60 (lub gdziekolwiek jest w pamięci)
0x01 ║ ibid
0x02 ║ ibid
0x03 ║ ibid
Jak widać powyżej, wszystko, co musisz zrobić, aby skopiować coś takiego, jest prawie tak proste, jak skopiowanie bajtu po bajcie. Dzięki Array.prototype.push.apply jest to znacznie więcej niż zwykłe kopiowanie i wklejanie danych. „.Apply” musi sprawdzić każdy indeks w tablicy i przekonwertować go na zestaw argumentów przed przekazaniem go do Array.prototype.push. Następnie Array.prototype.push musi dodatkowo przydzielać więcej pamięci za każdym razem, a (w przypadku niektórych implementacji przeglądarki) może nawet przeliczyć niektóre dane wyszukiwania pozycji dla rzadkości.
Alternatywnym sposobem myślenia o tym jest to. Pierwszy z nich to duży stos zszytych ze sobą papierów. Druga tablica źródłowa to także kolejny duży stos papierów. Czy byłoby to dla ciebie szybsze
- Idź do sklepu, kup wystarczającą ilość papieru potrzebną na kopię każdej tablicy źródłowej. Następnie umieść stosy papieru z każdej tablicy źródłowej w maszynie kopiującej i zszyj uzyskane dwie kopie razem.
- Idź do sklepu, kup wystarczającą ilość papieru na jedną kopię pierwszej tablicy źródłowej. Następnie ręcznie skopiuj tablicę źródłową na nowy papier, wypełniając puste puste miejsca. Następnie wróć do sklepu, kup wystarczającą ilość papieru na drugi układ źródłowy. Następnie przejdź przez drugą tablicę źródłową i skopiuj ją, upewniając się, że nie ma pustych przerw w kopii. Następnie zszyj wszystkie skopiowane dokumenty razem.
W powyższej analogii opcja nr 1 reprezentuje Array.prototype.concat, a nr 2 reprezentuje Array.prototype.push.apply. Przetestujmy to z podobnym JSperf, różniącym się tylko tym, że ten testuje metody na rzadkich tablicach, a nie na stałych tablicach. Można go znaleźć tutaj .
Dlatego opieram swój przypadek, że przyszłość wydajności dla tego konkretnego przypadku użycia nie leży w Array.prototype.push, ale raczej w Array.prototype.concat.
𝗖𝗹𝗮𝗿𝗶𝗳𝗶𝗰𝗮𝘁𝗶𝗼𝗻𝘀
𝗦𝗽𝗮𝗿𝗲 𝗔𝗿𝗿𝗮𝘆𝘀
Gdy po prostu brakuje niektórych członków tablicy. Na przykład:
// 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]);
Alternatywnie, javascript pozwala łatwo inicjować zapasowe tablice.
var mySparseArray = ["foo",,,,,,,,,,undefined,{},10,,,,,"bar"];
𝗔𝗿𝗿𝗮𝘆-𝗟𝗶𝗸𝗲𝘀
Tablicowy to obiekt, który ma przynajmniej length
właściwość, ale nie został zainicjowany przy pomocy new Array
lub []
; Na przykład poniższe obiekty są klasyfikowane jako tablicowe.
{0: „foo”, 1: „bar”, długość: 2}
document.body.children
nowy Uint8Array (3)
- Jest to podobne do tablicy, ponieważ chociaż jest to tablica (n) (typowana), wymuszenie jej na tablicę zmienia konstruktor.
(function () {return arguments}) ()
Obserwuj, co się dzieje, używając metody, która wymusza zamiany tablic na tablice takie jak plasterek.
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}() ));
- UWAGA: Złą praktyką jest wywoływanie wycinka argumentów funkcji ze względu na wydajność.
Obserwuj, co się dzieje, używając metody, która nie zmusza tablic do tablic takich jak 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}() ));