Chrome 59更新:正如我在下面的回答中所预测的那样,使用新的优化编译器后,绑定不再变慢。这是带有详细信息的代码:https : //codereview.chromium.org/2916063002/
大多数时候都没有关系。
除非您创建一个应用程序,否则.bind
瓶颈就不会困扰我。在大多数情况下,可读性比纯粹的性能更为重要。我认为使用本机.bind
通常会提供更具可读性和可维护性的代码-这是一大优势。
但是,是的,重要的.bind
是- 速度较慢
是的,.bind
它比闭包要慢得多-至少在Chrome中,至少是在中当前实现的方式中v8
。我个人有时不得不切换到Node.JS来解决性能问题(更一般地说,在性能密集型情况下,闭包有点慢)。
为什么?因为该.bind
算法比使用另一个函数包装一个函数并使用.call
或复杂得多.apply
。(有趣的是,它还会返回一个toString设置为[native function]的函数)。
从规范的角度和从实现的角度来看,有两种方法可以查看。让我们观察两者。
- 令Target为该值。
- 如果IsCallable(Target)为false,则抛出TypeError异常。
- 令A为按顺序在thisArg(arg1,arg2等)之后提供的所有参数值的新内部列表(可能为空)。
...
(21.用参数“参数”调用F的[[DefineOwnProperty]]内部方法,PropertyDescriptor {[[Get]]:thrower,[[Set]]:thrower,[[Enumerable]]:false,[[Configurable] ]:false}和false。
(22.返回F。
看起来非常复杂,不只是包装。
让我们检查FunctionBind
一下v8(chrome JavaScript引擎)源代码:
function FunctionBind(this_arg) {
if (!IS_SPEC_FUNCTION(this)) {
throw new $TypeError('Bind must be called on a function');
}
var boundFunction = function () {
"use strict";
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") {
var old_length = this.length;
if ((typeof old_length === "number") &&
((old_length >>> 0) === old_length)) {
var argc = %_ArgumentsLength();
if (argc > 0) argc--;
new_length = old_length - argc;
if (new_length < 0) new_length = 0;
}
}
var result = %FunctionBindArguments(boundFunction, this,
this_arg, new_length);
return result;
在执行过程中,我们可以看到很多昂贵的东西。即%_IsConstructCall()
。当然,必须遵守规范-但在许多情况下,它也比简单包装慢。
另一个注意,调用.bind
也略有不同,规范说明“使用Function.prototype.bind创建的函数对象没有原型属性,或者内部没有[[Code]],[[FormalParameters]]和[[Scope]]内部属性”