![](/img/trans.png)
[英]Why is passing in a variable (as a ref) slower than accessing the same variable defined in a closure?
[英]Why is bind slower than a closure?
之前有张贴问过Javascript中的Function.bind vs Closure:如何选择?
并部分收到了这个答案,这似乎表明 bind 应该比闭包更快:
范围遍历意味着,当您要获取存在于不同范围中的值(变量、对象)时,会增加额外的开销(代码执行速度变慢)。
使用绑定,您正在调用具有现有作用域的函数,因此不会发生作用域遍历。
两个 jsperfs 表明 bind 实际上比closure慢得多。
这是作为对上述内容的评论发布的
而且,我决定编写自己的 jsperf
那么为什么绑定这么慢(铬为 70+%)?
既然它不是更快并且闭包可以达到同样的目的,那么应该避免绑定吗?
Chrome 59 更新:正如我在下面的答案中预测的那样,使用新的优化编译器,bind 不再变慢。 这是包含详细信息的代码: https : //codereview.chromium.org/2916063002/
除非您正在创建一个.bind
是瓶颈的应用程序,否则我不会打扰。 在大多数情况下,可读性比纯粹的性能重要得多。 我认为使用原生.bind
通常会提供更具可读性和可维护性的代码 - 这是一个很大的优势。
.bind
速度较慢是的, .bind
比闭包慢得多 - 至少在 Chrome 中,至少在它在v8
实现的当前方式中。 我个人有时不得不在 Node.JS 中切换以解决性能问题(更一般地说,在性能密集的情况下,闭包有点慢)。
为什么? 因为.bind
算法是很多比包装的功能与其他功能,并使用复杂的.call
或.apply
。 (有趣的是,它还返回一个将 toString 设置为 [native function] 的函数)。
有两种方式来看待这一点,从规范的角度和从实现的角度来看。 让我们观察两者。
- 让 Target 为 this 值。
- 如果 IsCallable(Target) 为 false,则抛出 TypeError 异常。
- 令 A 是一个新的(可能是空的)内部列表,其中包含在 thisArg(arg1、arg2 等)之后提供的所有参数值,按顺序排列。
...
(21.调用F的[[DefineOwnProperty]]内部方法,参数为“arguments”,PropertyDescriptor {[[Get]]: thrower, [[Set]]: thrower, [[Enumerable]]: false, [[Configurable] ]:假},假。
(22. 返回 F。
看起来很复杂,不仅仅是一个包装。
让我们检查 v8(chrome JavaScript 引擎)源代码中的FunctionBind
:
function FunctionBind(this_arg) { // Length is 1.
if (!IS_SPEC_FUNCTION(this)) {
throw new $TypeError('Bind must be called on a function');
}
var boundFunction = function () {
// Poison .arguments and .caller, but is otherwise not detectable.
"use strict";
// This function must not use any object literals (Object, Array, RegExp),
// since the literals-array is being used to store the bound data.
if (%_IsConstructCall()) {
return %NewObjectFromBound(boundFunction);
}
var bindings = %BoundFunctionGetBindings(boundFunction);
var argc = %_ArgumentsLength();
if (argc == 0) {
return %Apply(bindings[0], bindings[1], bindings, 2, bindings.length - 2);
}
if (bindings.length === 2) {
return %Apply(bindings[0], bindings[1], arguments, 0, argc);
}
var bound_argc = bindings.length - 2;
var argv = new InternalArray(bound_argc + argc);
for (var i = 0; i < bound_argc; i++) {
argv[i] = bindings[i + 2];
}
for (var j = 0; j < argc; j++) {
argv[i++] = %_Arguments(j);
}
return %Apply(bindings[0], bindings[1], argv, 0, bound_argc + argc);
};
%FunctionRemovePrototype(boundFunction);
var new_length = 0;
if (%_ClassOf(this) == "Function") {
// Function or FunctionProxy.
var old_length = this.length;
// FunctionProxies might provide a non-UInt32 value. If so, ignore it.
if ((typeof old_length === "number") &&
((old_length >>> 0) === old_length)) {
var argc = %_ArgumentsLength();
if (argc > 0) argc--; // Don't count the thisArg as parameter.
new_length = old_length - argc;
if (new_length < 0) new_length = 0;
}
}
// This runtime function finds any remaining arguments on the stack,
// so we don't pass the arguments object.
var result = %FunctionBindArguments(boundFunction, this,
this_arg, new_length);
// We already have caller and arguments properties on functions,
// which are non-configurable. It therefore makes no sence to
// try to redefine these as defined by the spec. The spec says
// that bind should make these throw a TypeError if get or set
// is called and make them non-enumerable and non-configurable.
// To be consistent with our normal functions we leave this as it is.
// TODO(lrn): Do set these to be thrower.
return result;
我们可以在实现中看到一堆昂贵的东西。 即%_IsConstructCall()
。 这当然是遵守规范所必需的——但在许多情况下,这也使它比简单的包装更慢。
另一方面,调用.bind
也略有不同,规范说明“使用 Function.prototype.bind 创建的函数对象没有原型属性或 [[Code]]、[[FormalParameters]] 和 [[Scope] ] 内部属性”
我只想在这里给出一点观点:
请注意,虽然bind()
ing很慢,但一旦绑定就调用函数不是!
我在 Linux 上的 Firefox 76.0 中的测试代码:
//Set it up. q = function(r, s) { }; r = {}; s = {}; a = []; for (let n = 0; n < 1000000; ++n) { //Tried all 3 of these. //a.push(q); //a.push(q.bind(r)); a.push(q.bind(r, s)); } //Performance-testing. s = performance.now(); for (let x of a) { x(); } e = performance.now(); document.body.innerHTML = (e - s);
因此,虽然.bind()
确实比不绑定慢了大约 2 倍(我也测试过),但上面的代码对于所有 3 种情况(绑定 0、1 或 2 个变量)花费的时间相同.
就我个人而言,我不关心.bind()
在我当前的用例中是否很慢,我关心的是一旦这些变量已经绑定到函数上被调用的代码的性能。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.