我一直在考虑underscorejs:
var optimizeCb = function(func, context, argCount) {
if (context === void 0) return func;
switch (argCount == null ? 3 : argCount) {
case 1: return function(value) {
return func.call(context, value);
};
case 2: return function(value, other) {
return func.call(context, value, other);
};
case 3: return function(value, index, collection) {
return func.call(context, value, index, collection);
};
case 4: return function(accumulator, value, index, collection) {
return func.call(context, accumulator, value, index, collection);
};
}
return function() {
return func.apply(context, arguments);
};
};
因此,显然此优化仅适用于设置this
值的回调(此处称为context
)。这与直接调用call
到回调有什么不同?这如何才能提高性能?
如果优化仅对旧版JS引擎有效,那就没关系。我只是想知道。
我可能不清楚这个问题。这就是我的意思。我们来看一个使用optimizeCb的例子:
_.each = _.forEach = function(obj, iteratee, context) {
iteratee = optimizeCb(iteratee, context); //REMOVE this
var i, length;
if (isArrayLike(obj)) {
for (i = 0, length = obj.length; i < length; i++) {
iteratee(obj[i], i, obj);
//REPLACE with iteratee.call(context, obj[i], i, obj);
}
} else {
var keys = _.keys(obj);
for (i = 0, length = keys.length; i < length; i++) {
iteratee(obj[keys[i]], keys[i], obj);
}
}
return obj;
};
请参阅2条评论:iteratee = optimizeCb(iteratee, context); //REMOVE this
和iteratee(obj[i], i, obj); //REPLACE with iteratee.call(context, obj[i], i, obj);
。我理解论点很慢,适用很慢。但我不知道参数和呼叫与应用如何在这里发挥作用?我认为两种方法没有区别。
我认为关键问题是如果将回调传递给某个下划线方法,那么签名就已经知道了。例如,传递到_.each
的回调必须有function(value, index, collection)
。调用optimizeCb的方式证实了这一观察结果:如果optimizeCb的调用者能够提供argCount参数(留空则表示它是3),它就知道它是哪个签名。
有人可以进一步阐述吗?非常感谢!
答案 0 :(得分:2)
他们可能会这样做的三个原因:
在较旧的JavaScript引擎上访问arguments
伪阵列代价很高。真的很贵。比如,几个数量级的代价高昂。 : - )
我不认为现代引擎上会有巨大的成本,特别是在严格模式下,这会消除arguments
和正式函数参数之间的实时链接。
由于Moogs指出comment,apply
慢于call
。基于this test,看起来它介于两半之间,成本增加一倍。所以不是arguments
(当天回归)的数量级或者两次,但仍然更快。
如果context
为undefined
,则会返回未更改的函数(即初始if (context == void 0) return func;
),因此没有.call
或 .apply
参与其中。
所以这样做有两个方面:
A)如果回调不需要特定的this
,它会通过简单的函数调用直接使用回调。如果回调 需要特定的this
,他们会创建一个函数,他们可以调用相同的方式使用正确的this
,从而节省context
的传递}参数并简化了调用代码。
B)他们通过自定义包装函数来避免访问arguments
并使用apply
来获取常见数量的参数:采用1-4参数的回调可以避免成本,回调数为0或更多超过4个参数会产生成本。