并且部分地收到了这个答案,这似乎表明bind应该比闭包更快:
范围遍历意味着,当您要获取存在于另一个范围中的值(变量,对象)时,因此会增加额外的开销(代码执行起来会变慢)。
使用bind,您正在使用现有范围调用函数,这样就不会发生范围遍历。
两个jsperfs表示bind实际上比闭包慢得多。
这是对以上内容的评论
而且,我决定编写自己的jsperf
那么,为什么结合速度这么慢(铬含量超过70%)?
由于速度不快并且闭包可以达到相同的目的,是否应避免绑定?
Chrome59更新:正如我在下面的回答中所预测的那样,使用新的优化编译器后,绑定不再变慢。 :
除非您要创建应用程序,否则.bind瓶颈就不会困扰我。在大多数情况下,可读性比纯粹的性能更为重要。我认为使用本机.bind通常会提供更具可读性和可维护性的代码-这是一大优势。
.bind
是的,.bind它比闭包要慢得多-至少在Chrome中,至少是在中当前实现的方式中v8。我个人有时不得不切换Node.JS来解决性能问题(通常,在性能密集型情况下,闭包有点慢)。
v8
为什么?因为该.bind算法比使用另一个函数包装一个函数并使用.call或复杂得多.apply。(有趣的是,它还会返回一个toString设置为[nativefunction]的函数)。
.call
.apply
从规范的角度和从实现的角度来看,有两种方法可以查看。让我们观察两者。
…
(21.用参数“参数”调用F的[[DefineOwnProperty]]内部方法,PropertyDescriptor {[[Get]]:thrower,[[Set]]:thrower,[[Enumerable]]:false,[[Configurable] ]:false}和false。
(22.返回F。
看起来很复杂,不只是包装。
其次,让我们看看如何在Chrome中实现它。
让我们检查FunctionBind一下v8(chromeJavaScript引擎)源代码:
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()。当然,这是要遵守规范所必需的-但在许多情况下,它也比简单包装慢。
%_IsConstructCall()
另一个注意,调用.bind也略有不同,规范说明“使用Function.prototype.bind创建的函数对象没有原型属性,或者内部没有[[Code]],[[FormalParameters]]和[[Scope]]内部属性”