𝗥𝗲𝘀𝘂𝗹𝘁𝘀
事实是,在jsperf上进行了性能测试并检查了控制台中的某些内容。为了进行研究,使用了网站irt.org。下面是所有这些来源的集合,底部是示例函数。
╔═══════════════╦══════╦═════════════════╦════════ ═══════╦═════════╦══════════╗
║方法║Concat║slice&push.apply║push.applyx2║ForLoop║Spread║
╠═══════════════╬══════╬═════════════════╬════════ ═══════╬═════════╬══════════╣
║mOps /秒║179║104║76║81║28║
╠═══════════════╬══════╬═════════════════╬════════ ═══════╬═════════╬══════════╣
rs稀疏数组║是!║Only切片║║没有可能2 ║no║
║保持稀疏║array(1st arg)║║
╠═══════════════╬══════╬═════════════════╬════════ ═══════╬═════════╬══════════╣
║支持║MSIE4║MSIE5.5║MSIE 5.5║MSIE 4║Edge12║
║(源)║NNav4║NNav4.06║NNAV 4.06║NNAV 3║ MSIE NNAV ║
╠═══════════════╬══════╬═════════════════╬════════ ═══════╬═════════╬══════════╣
║类似数组的行为║否║仅按pushed是!║是的!have如果有║
║like阵列║║array(第二ARG)║║║iterator 1 ║
╚═══════════════牛皮══════牛皮═════════════════牛皮════════ ═══════牛皮═════════牛皮══════════╝
1如果类数组对象没有Symbol.iterator属性,则尝试
传播它会引发异常。
2取决于代码。以下示例代码“ YES”保留了稀疏性。
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循环,因为它既是性能第二高的方法,又是保留稀疏数组的唯一其他方法。下面,我们将快速浏览稀疏数组和类似数组的含义,以消除混乱。
𝗙𝘂𝘁𝘂𝗿𝗲
最初,有些人可能会认为这是fl幸,浏览器供应商最终将绕过优化Array.prototype.push的速度,使其足以击败Array.prototype.concat。错误!Array.prototype.concat总是更快(至少在原则上来说如此),因为它是对数据的简单复制-n-粘贴。以下是32位阵列实现的外观的简化的persuado-visual图表(请注意,实际实现要复杂得多)
字节║数据在这里
═════╬═══════════
0x00║int nonNumericPropertiesLength = 0x00000000
0x01║同上
0x02║同上
0x03║同上
0x00║int长度= 0x00000001
0x01║同上
0x02║同上
0x03║同上
0x00║int valueIndex = 0x00000000
0x01║同上
0x02║同上
0x03║同上
0x00║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每次必须另外分配更多的内存,并且(对于某些浏览器实现)甚至可能出于稀疏性而重新计算一些位置查找数据。
另一种思考的方式是这样。源阵列之一是装订在一起的一大堆纸。源阵列2也是另一叠大文件。你会更快吗
- 去商店,购买足够的纸张来复制每个源阵列。然后,将每个源阵列纸叠通过复印机,将装订后的两个副本装订在一起。
- 去商店,购买足够的纸张以获取第一个源阵列的单个副本。然后,手动将源阵列复制到新纸张上,确保填充任何空白的稀疏斑点。然后,回到商店,为第二个来源阵列购买足够的纸张。然后,检查第二个源数组并复制它,同时确保副本中没有空白。然后,将所有复印的纸张装订在一起。
在上面的类比中,选项#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(){返回参数})()
观察使用将像数组一样强制转换为像slice这样的数组的方法会发生什么。
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}() ));