A previous poster asked Function.bind vs Closure in Javascript : how to choose?
之前的一张海报是关于功能的。Javascript中的bind vs Closure:如何选择?
and received this answer in part, which seems to indicate bind should be faster than a closure:
并得到了部分答案,这似乎表明bind要比closure快:
Scope traversal means, when you are reaching to grab a value (variable,object) that exists in a different scope, therefore additional overhead is added (code becomes slower to execute).
范围遍历的意思是,当您要获取在不同范围内存在的值(变量、对象)时,会增加额外的开销(代码执行的速度会变慢)。
Using bind, you 're calling a function with an existing scope, so that scope traversal does not take place.
使用bind时,您将使用现有的作用域来调用函数,因此不会发生遍历范围。
Two jsperfs suggest that bind is actually much, much slower than a closure.
两个jsperfs建议绑定实际上比关闭要慢得多。
This was posted as a comment to the above
这是对上面的评论
And, I decided to write my own jsperf
我决定写我自己的jsperf
So why is bind so much slower (70+% on chromium)?
那么为什么绑定速度要慢得多(铬是70% +%)呢?
Since it is not faster and closures can serve the same purpose, should bind be avoided?
既然闭包不会更快,而且可以达到同样的目的,那么应该避免绑定吗?
1 个解决方案
#1
128
Chrome 59 update: As I predicted in the answer below bind is no longer slower with the new optimizing compiler. Here's the code with details: https://codereview.chromium.org/2916063002/
Chrome 59更新:正如我在下面的答案中所预测的,与新的优化编译器绑定不再更慢。下面是详细的代码:https://codereview.chromium.org/2916063002/
Most of the time it does not matter.
Unless you're creating an application where .bind
is the bottleneck I wouldn't bother. Readability is much more important than sheer performance in most cases. I think that using native .bind
usually provides for more readable and maintainable code - which is a big plus.
除非您正在创建的应用程序中。bind是瓶颈,否则我不会麻烦。在大多数情况下,可读性比纯粹的性能更重要。我认为使用本机.bind通常提供更可读和可维护的代码——这是一个很大的优点。
However yes, when it matters - .bind
is slower
Yes, .bind
is considerably slower than a closure - at least in Chrome, at least in the current way it's implemented in v8
. I've personally had to switch in Node.JS for performance issues some times (more generally, closures are kind of slow in performance intensive situations).
是的,.bind比闭包慢得多——至少在Chrome中是这样,至少在v8中是这样实现的。我个人不得不切换到Node。JS对于性能问题有时(更普遍地说,闭包在性能密集型的情况下是比较慢的)。
Why? Because the .bind
algorithm is a lot more complicated than wrapping a function with another function and using .call
or .apply
. (Fun fact, it also returns a function with toString set to [native function]).
为什么?因为.bind算法要比用另一个函数包装一个函数并使用.call或.apply复杂得多。(有趣的是,它还返回一个将toString设置为[native function]的函数)。
There are two ways to look at this, from the specification point of view, and from the implementation point of view. Let's observe both.
从规范的角度来看,从实现的角度来看,有两种方式。让我们观察两种。
First, let's look at the bind algorithm defined in the specification:
- Let Target be the this value.
- 设Target为这个值。
- If IsCallable(Target) is false, throw a TypeError exception.
- 如果IsCallable(Target)为false,则抛出一个TypeError异常。
- Let A be a new (possibly empty) internal list of all of the argument values provided after thisArg (arg1, arg2 etc), in order.
- 让A是一个新的(可能是空的)内部列表,其中包含在thisArg (arg1, arg2等)之后的所有参数值。
...
…
(21. Call the [[DefineOwnProperty]] internal method of F with arguments "arguments", PropertyDescriptor {[[Get]]: thrower, [[Set]]: thrower, [[Enumerable]]: false, [[Configurable]]: false}, and false.
(21。用参数“arguments”调用[DefineOwnProperty] F的内部方法,PropertyDescriptor {[[]]: thrower, [[Set]]: thrower, [Enumerable]: false,[可配置]:false}, false。
(22. Return F.
(22。返回F。
Seems pretty complicated, a lot more than just a wrap.
看起来很复杂,不仅仅是包装。
Second , let's see how it's implemented in Chrome.
Let's check FunctionBind
in the v8 (chrome JavaScript engine) source code:
让我们检查一下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;
We can see a bunch of expensive things here in the implementation. Namely %_IsConstructCall()
. This is of course needed to abide to the specification - but it also makes it slower than a simple wrap in many cases.
在实现中我们可以看到很多昂贵的东西。即% _IsConstructCall()。当然,这需要遵守规范——但在许多情况下,这也使它比简单的包装慢。
On another note, calling .bind
is also slightly different, the spec notes "Function objects created using Function.prototype.bind do not have a prototype property or the [[Code]], [[FormalParameters]], and [[Scope]] internal properties"
另一方面,调用.bind也略有不同,spec指出“使用Function.prototype创建函数对象。绑定没有原型属性或[[[Code]]]、[[FormalParameters]和[[[Scope]]内部属性"
#1
128
Chrome 59 update: As I predicted in the answer below bind is no longer slower with the new optimizing compiler. Here's the code with details: https://codereview.chromium.org/2916063002/
Chrome 59更新:正如我在下面的答案中所预测的,与新的优化编译器绑定不再更慢。下面是详细的代码:https://codereview.chromium.org/2916063002/
Most of the time it does not matter.
Unless you're creating an application where .bind
is the bottleneck I wouldn't bother. Readability is much more important than sheer performance in most cases. I think that using native .bind
usually provides for more readable and maintainable code - which is a big plus.
除非您正在创建的应用程序中。bind是瓶颈,否则我不会麻烦。在大多数情况下,可读性比纯粹的性能更重要。我认为使用本机.bind通常提供更可读和可维护的代码——这是一个很大的优点。
However yes, when it matters - .bind
is slower
Yes, .bind
is considerably slower than a closure - at least in Chrome, at least in the current way it's implemented in v8
. I've personally had to switch in Node.JS for performance issues some times (more generally, closures are kind of slow in performance intensive situations).
是的,.bind比闭包慢得多——至少在Chrome中是这样,至少在v8中是这样实现的。我个人不得不切换到Node。JS对于性能问题有时(更普遍地说,闭包在性能密集型的情况下是比较慢的)。
Why? Because the .bind
algorithm is a lot more complicated than wrapping a function with another function and using .call
or .apply
. (Fun fact, it also returns a function with toString set to [native function]).
为什么?因为.bind算法要比用另一个函数包装一个函数并使用.call或.apply复杂得多。(有趣的是,它还返回一个将toString设置为[native function]的函数)。
There are two ways to look at this, from the specification point of view, and from the implementation point of view. Let's observe both.
从规范的角度来看,从实现的角度来看,有两种方式。让我们观察两种。
First, let's look at the bind algorithm defined in the specification:
- Let Target be the this value.
- 设Target为这个值。
- If IsCallable(Target) is false, throw a TypeError exception.
- 如果IsCallable(Target)为false,则抛出一个TypeError异常。
- Let A be a new (possibly empty) internal list of all of the argument values provided after thisArg (arg1, arg2 etc), in order.
- 让A是一个新的(可能是空的)内部列表,其中包含在thisArg (arg1, arg2等)之后的所有参数值。
...
…
(21. Call the [[DefineOwnProperty]] internal method of F with arguments "arguments", PropertyDescriptor {[[Get]]: thrower, [[Set]]: thrower, [[Enumerable]]: false, [[Configurable]]: false}, and false.
(21。用参数“arguments”调用[DefineOwnProperty] F的内部方法,PropertyDescriptor {[[]]: thrower, [[Set]]: thrower, [Enumerable]: false,[可配置]:false}, false。
(22. Return F.
(22。返回F。
Seems pretty complicated, a lot more than just a wrap.
看起来很复杂,不仅仅是包装。
Second , let's see how it's implemented in Chrome.
Let's check FunctionBind
in the v8 (chrome JavaScript engine) source code:
让我们检查一下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;
We can see a bunch of expensive things here in the implementation. Namely %_IsConstructCall()
. This is of course needed to abide to the specification - but it also makes it slower than a simple wrap in many cases.
在实现中我们可以看到很多昂贵的东西。即% _IsConstructCall()。当然,这需要遵守规范——但在许多情况下,这也使它比简单的包装慢。
On another note, calling .bind
is also slightly different, the spec notes "Function objects created using Function.prototype.bind do not have a prototype property or the [[Code]], [[FormalParameters]], and [[Scope]] internal properties"
另一方面,调用.bind也略有不同,spec指出“使用Function.prototype创建函数对象。绑定没有原型属性或[[[Code]]]、[[FormalParameters]和[[[Scope]]内部属性"