[].map.call() VS Array.prototype.map.call()?
[].map.call() VS Array.prototype.map.call()?
为什么一个比另一个更受欢迎?
[].map.call(...)
Array.prototype.map.call(...)
在jsPerf中进行快速测试表明Array.prototype方式的性能更高,尽管我在某处读到jsPerf结果可能具有欺骗性。不是在这里讨论jsPerf,而只是寻找一些关于为什么一个比另一个更受欢迎的见解。谢谢!
[].map
和 Array.prototype.map
的值(在没有恶作剧的情况下)是相同的。表达式[].map
涉及(至少在概念上;可以优化它)新 Array 实例的构造,因此可能会产生(非常小的)性能影响。
表达式[].map
创建一个新的空数组,然后引用其"map"属性。数组实例没有"map"属性,除非你添加一个,所以它不会在对象本身上找到。因此,运行时将检查原型链上的下一件事,当然是Array.prototype
对象。运行时将在那里找到一个"map"属性 - 特别是Array.prototype.map
。这就是为什么它们是一回事。
类似的等价适用于{}.toString
和Object.prototype.toString
。使用模式的主要区别可能是{}.toString
出现在表达式的最开头可能会导致问题,因为在这种情况下,前导{
将被视为语句块{
,而不是对象初始化{
。但是,{}.toString
的典型用法使得它不太可能需要启动表达式。因此
console.log({}.toString.call(someMysteryObject));
工作一样好
console.log(Object.prototype.toString.call(someMysteryObject));
在性能方面,在.map()
的情况下,使用该方法时隐含的函数调用的开销几乎肯定会完全压倒在开始时查找对.map()
函数的引用的两种方式之间的性能差异。
基准测试
1. 背景
我做了一个基本的基准测试,看看我是否可以发现这四个不同语句之间的任何性能差异:
-
dataSet.map(function)
-
dataSet.map.call(dataSet, function)
-
[].map.call(dataSet, function)
-
Array.prototype.map.call(dataSet, function)
2. 方法
我在一个独立的沙箱中执行了四个语句中的每一个 1,000,000 次,并比较了之后的处理时间。
我使用以下计时器函数来确定处理时间:
var timer = function(name) {
var start = new Date();
return {
stop: function() {
var end = new Date();
var time = end.getTime() - start.getTime();
console.log('Timer:', name, 'finished in', time, 'ms');
}
}
};
<小时 />3. 测试环境
我在一台装有英特尔 i7 四核 CPU 的 2 岁华硕笔记本电脑上运行了测试。
我在以下两个浏览器上用Linux Ubuntu进行了测试:
- 火狐 43.0
- 铬 45.0
4. 场景
4.1. dataSet.map(function)
场景
var t = timer('Benchmark'); // <-- START BENCHMARK
var reformattedArray;
var rObj;
var kvArray = [{key:1, value:10}, {key:2, value:20}, {key:3, value: 30}];
for(var i = 0; i < 1000000; i++) {
reformattedArray = kvArray.map(function(obj){
rObj = {};
rObj[obj.key] = obj.value;
return rObj;
});
}
t.stop(); // <-- STOP BENCHMARK
(另见这首小提琴)
4.2. dataSet.map.call(dataSet, function)
场景
var t = timer('Benchmark'); // <-- START BENCHMARK
var reformattedArray;
var rObj;
var kvArray = [{key:1, value:10}, {key:2, value:20}, {key:3, value: 30}];
for(var i = 0; i < 1000000; i++) {
reformattedArray = kvArray.map.call(kvArray, function(obj){
rObj = {};
rObj[obj.key] = obj.value;
return rObj;
});
}
t.stop(); // <-- STOP BENCHMARK
(另见这首小提琴)
4.3. [].map.call(dataSet, function)
场景
var t = timer('Benchmark'); // <-- START BENCHMARK
var reformattedArray;
var rObj;
var kvArray = [{key:1, value:10}, {key:2, value:20}, {key:3, value: 30}];
for(var i = 0; i < 1000000; i++) {
reformattedArray = [].map.call(kvArray, function(obj){
rObj = {};
rObj[obj.key] = obj.value;
return rObj;
});
}
t.stop(); // <-- STOP BENCHMARK
(另见这首小提琴)
4.4. Array.prototype.map.call(dataSet, function)
场景
var t = timer('Benchmark'); // <-- START BENCHMARK
var reformattedArray;
var rObj;
var kvArray = [{key:1, value:10}, {key:2, value:20}, {key:3, value: 30}];
for(var i = 0; i < 1000000; i++) {
reformattedArray = Array.prototype.map.call(kvArray, function(obj){
rObj = {};
rObj[obj.key] = obj.value;
return rObj;
});
}
t.stop(); // <-- STOP BENCHMARK
(另见这首小提琴)
<小时 />5. 中间结果
我没有注意到四种情况之间的任何性能差异,但是我确实注意到Firefox比Chrome快约4倍(对于这两种情况)。
更具体地说,Chrome 处理每个场景大约需要 1 秒,而 Fixefox 只需要 0.25 秒。
为了评估这些浏览器差异是否特定于map
方法,我进一步简化了测试。
6. 场景 v2
6.1. dataSet.map(function)
的场景 v2
var t = timer('Benchmark'); // <-- START BENCHMARK
var reformattedArray;
var rObj;
var kvArray = [{key:1, value:10}, {key:2, value:20}, {key:3, value: 30}];
for(var i = 0; i < 1000000; i++) {
kvArray.map(function(obj){
return rObj;
});
}
t.stop(); // <-- STOP BENCHMARK
(另见这首小提琴)
6.2. dataSet.map.call(dataSet, function)
的场景 v2
var t = timer('Benchmark'); // <-- START BENCHMARK
var reformattedArray;
var rObj;
var kvArray = [{key:1, value:10}, {key:2, value:20}, {key:3, value: 30}];
for(var i = 0; i < 1000000; i++) {
kvArray.map.call(kvArray, function(obj){
return rObj;
});
}
t.stop(); // <-- STOP BENCHMARK
(另见这首小提琴)
6.3. [].map.call(dataSet, function)
的场景 v2
var t = timer('Benchmark'); // <-- START BENCHMARK
var reformattedArray;
var rObj;
var kvArray = [{key:1, value:10}, {key:2, value:20}, {key:3, value: 30}];
for(var i = 0; i < 1000000; i++) {
[].map.call(kvArray, function(obj){
return rObj;
});
}
t.stop(); // <-- STOP BENCHMARK
(另见这首小提琴)
6.4. Array.prototype.map.call(dataSet, function)
的场景 v2
var t = timer('Benchmark'); // <-- START BENCHMARK
var reformattedArray;
var rObj;
var kvArray = [{key:1, value:10}, {key:2, value:20}, {key:3, value: 30}];
for(var i = 0; i < 1000000; i++) {
Array.prototype.map.call(kvArray, function(obj){
return rObj;
});
}
t.stop(); // <-- STOP BENCHMARK
(另见这首小提琴)
<小时 />7. 最终结果
现在,Chrome 运行这些场景大约需要 0.275 秒,Firefox 大约需要 0.035 秒。这意味着Firefox比Chrome快7倍以上。
同样,使用相同的浏览器时,四种方案中每种方案的性能没有明显差异。
- 在下划线中使用_(obj).map(callback)和_.map(obj,callback)之间的区别
- 使用map来检查是否为真'不起作用
- 回调函数中传递参数的困难(Google Map API Markers)
- call()和apply()实际上是用来欺骗方法处理类似数组的对象的
- 使用API在我的网站Google-Map上显示搜索地址的纬度和经度
- How to declare a Map containing certain properties with flow
- 如何将i18n.map转换为json对象
- 使用filter和map方法将数组中某些元素的第一个字母大写-JavaScript
- 节点中的Map Reduce
- 用Jquery map和moment js制作一个简单的时间线
- 谷歌地图API v3:Initial View is Fine,but Gray Box with No Map if
- JavaScript中的这个call()-方法是如何工作的
- _.map在返回时覆盖值
- 为什么使用Array.prototype.map.call而不是Array.map.call
- [].map.call() VS Array.prototype.map.call()?
- array.forEach.call vs array.map.call
- Rails和React: TypeError: Cannot call method 'map'的定义
- js:为什么用Array.prototype.map.call代替map() ?
- 为什么不't[“A”,“B”,“C”].map(String.protype.toLowerCase.call)
- 将function.prototype.call生成的函数作为参数传递时,Array.prototype.map不是函数