Chrome 59更新:正如我在下面的答案中预测的那样,使用新的优化编译器,bind不再慢了.以下是代码和详细信息:https://codereview.chromium.org/2916063002/
大多数时候,这并不重要.
除非你正在创建一个以.bind
为瓶颈的应用程序,否则我不会费心.在大多数情况下,可读性比纯粹的性能更重要.我认为使用native .bind
通常可以提供更可读和可维护的代码,这是一个很大的优势.
However yes, when it matters - .bind
is slower
是的,.bind
比闭包慢得多——至少在Chrome中是这样,至少在v8
中实现的当前方式是这样.我个人不得不在 node 中切换.JS有时会出现性能问题(更一般地说,闭包在性能密集型的情况下有点慢).
为什么?因为.bind
算法比用另一个函数包装一个函数并使用.call
或.apply
复杂得多.(有趣的是,它还返回一个toString设置为[native function]的函数.).
从规范的Angular 和实现的Angular 来看,有两种方法.让我们观察两者.
- 将目标设为该值的最大值.
- 如果IsCallable(Target)为false,则抛出TypeError异常.
- A是一个新的(可能是空的)内部列表,其中包含在thisArg(arg1、arg2等)之后提供的所有参数值.
...
(21.使用参数"arguments"、PropertyDescriptor{[[Get]]:Trower、[[Set]]:Trower、[[Enumerable]]:false、[[Configurable]]:false}和false调用F的[[DefineOwnProperty]]内部方法.
(22)返回F.
看起来很复杂,不仅仅是一个Package.
让我们判断一下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]]内部属性"