(für die ursprüngliche Frage)
Zu den Fakten: ein leistungsprüfung bei jsperf und die Überprüfung einiger Dinge in der Konsole werden durchgeführt. Für die Forschung, die Website irt.org verwendet wird. Nachfolgend finden Sie eine Zusammenstellung all dieser Quellen sowie eine Beispielfunktion am Ende der Seite.
Method Concatslice&push.apply push.apply x2 ForLoop Spread
mOps/Sec 179 104 76 81 28
Sparse arrays YES! Only the sliced no Maybe2 no
kept sparse array (1st arg)
Support MSIE 4MSIE 5.5 MSIE 5.5 MSIE 4 Edge 12
([source](https://www.irt.org/xref/core_objects.htm)) NNav 4NNav 4.06 NNav 4.06 NNav 3 ~~MSIE~~ ~~NNav~~
Array-like actsno Only the pushed YES! YES! If have
like an array array (2nd arg) iterator1
1 If the array-like object does not have a _Symbol.iterator_ property, then trying
to spread it will throw an exception.
2 Depends on the code. The following example code "YES" preserves sparseness.
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;
}
Wie oben gesehen, würde ich argumentieren, dass Concat ist fast immer der Weg zu gehen, sowohl für die Leistung und die Fähigkeit, die Sparsamkeit der Ersatz-Arrays zu behalten. Dann, für Array-likes (wie DOMNodeLists wie document.body.children
), würde ich empfehlen, die for-Schleife zu verwenden, da sie sowohl die zweitleistungsfähigste als auch die einzige andere Methode ist, die spärliche Arrays beibehält. Im Folgenden werden wir kurz erläutern, was mit spärlichen Arrays und Array-ähnlichen Elementen gemeint ist, um Verwirrung zu vermeiden.
Zunächst mögen einige Leute denken, dass dies ein Zufall ist und dass die Browserhersteller irgendwann dazu kommen werden, Array.prototype.push so zu optimieren, dass es schnell genug ist, um Array.prototype.concat zu schlagen. FALSCH! Array.prototype.concat wird immer schneller sein (zumindest im Prinzip), weil es ein einfaches Kopieren und Einfügen der Daten ist. Nachfolgend sehen Sie ein vereinfachtes, visuelles Diagramm, wie eine 32-Bit-Array-Implementierung aussehen könnte (bitte beachten Sie, dass reale Implementierungen viel komplizierter sind)
Byte Data here
0x00 int nonNumericPropertiesLength = 0x00000000
0x01 ibid
0x02 ibid
0x03 ibid
0x00 int length = 0x00000001
0x01 ibid
0x02 ibid
0x03 ibid
0x00 int valueIndex = 0x00000000
0x01 ibid
0x02 ibid
0x03 ibid
0x00 int valueType = JS\_PRIMITIVE\_NUMBER
0x01 ibid
0x02 ibid
0x03 ibid
0x00 uintptr\_t valuePointer = 0x38d9eb60 (or whereever it is in memory)
0x01 ibid
0x02 ibid
0x03 ibid
Wie oben gesehen, ist alles, was Sie tun müssen, um so etwas zu kopieren, fast so einfach wie das Kopieren von Byte für Byte. Mit Array.prototype.push.apply ist es viel mehr als ein einfaches Kopieren und Einfügen der Daten. Das ".apply" muss jeden Index im Array überprüfen und ihn in eine Reihe von Argumenten umwandeln, bevor es ihn an Array.prototype.push weitergibt. Dann muss Array.prototype.push zusätzlich jedes Mal mehr Speicher zuweisen und (bei einigen Browser-Implementierungen) vielleicht sogar einige Positions-Lookup-Daten für die Sparsamkeit neu berechnen.
Man kann sich das auch anders vorstellen. Der Quellbereich eins ist ein großer Stapel zusammengehefteter Papiere. Der Quellbereich zwei ist ebenfalls ein großer Stapel von Papieren. Wäre es schneller für Sie, wenn Sie
- Gehen Sie in den Laden und kaufen Sie so viel Papier, wie Sie für eine Kopie jeder Quellengruppe benötigen. Legen Sie dann die Papierstapel der einzelnen Quellen durch einen Kopierer und heften Sie die beiden Kopien zusammen.
- Gehen Sie in den Laden und kaufen Sie genügend Papier für eine einzige Kopie des ersten Quellenfeldes. Kopieren Sie dann das Quellfeld von Hand auf das neue Papier und füllen Sie dabei alle leeren Stellen aus. Dann gehst du zurück in den Laden und kaufst genügend Papier für die zweite Quelle. Gehen Sie dann die zweite Quelle durch und kopieren Sie sie, wobei Sie darauf achten, dass keine Lücken in der Kopie entstehen. Heften Sie dann alle kopierten Papiere zusammen.
In der obigen Analogie steht die Option 1 für Array.prototype.concat und die Option 2 für Array.prototype.push.apply. Testen wir dies mit einem ähnlichen JSperf, der sich nur dadurch unterscheidet, dass er die Methoden über sparse Arrays und nicht über solid Arrays testet. Man kann es finden genau hier .
Daher bin ich der Meinung, dass die Zukunft der Leistung für diesen speziellen Anwendungsfall nicht in Array.prototype.push, sondern in Array.prototype.concat liegt.
Wenn bestimmte Elemente des Arrays einfach nicht vorhanden sind. Zum Beispiel:
// 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]);
Alternativ können Sie mit Javascript auch einfach Ersatz-Arrays initialisieren.
var mySparseArray = ["foo",,,,,,,,,,undefined,{},10,,,,,"bar"];
-
Ein Array-like ist ein Objekt, das mindestens eine length
Eigenschaft, wurde aber nicht initialisiert mit new Array
o []
Zum Beispiel werden die folgenden Objekte als array-like eingestuft.
{0: "foo", 1: "bar", length:2} document.body.children new Uint8Array(3)
-
Dies ist Array-ähnlich, denn obwohl es sich um ein (n) (typisiertes) Array handelt, ändert sich der Konstruktor, wenn man ihn in ein Array zwingt.
(function(){return arguments})()
Beobachten Sie, was passiert, wenn Sie eine Methode verwenden, die array-likes in arrays wie slice umwandelt.
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.lg(slice.call(document.body.children));
console.log(slice.call(new Uint8Array(3)));
console.log(slice.call( function(){return arguments}() ));
- HINWEIS: Der Aufruf von Slice auf Funktionsargumente ist aus Leistungsgründen eine schlechte Praxis.
Beobachten Sie, was passiert, wenn Sie eine Methode verwenden, die no Array-likes in Arrays wie concat umwandeln.
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}() ));