为什么绑定比关闭慢?

为什么绑定比关闭慢?,第1张

为什么绑定比关闭慢?

Chrome59更新:正如我在下面的回答中所预测的那样,使用新的优化编译器后,绑定不再变慢。
:

大多数时候都没有关系。

除非您要创建应用程序,否则

.bind
瓶颈就不会困扰我。在大多数情况下,可读性比纯粹的性能更为重要。我认为使用本机
.bind
通常会提供更具可读性和可维护性的代码-这是一大优势。

但是,是的,重要的
.bind
是- 速度较慢

是的,

.bind
它比闭包要慢得多-至少在Chrome中,至少是在中当前实现的方式中
v8
。我个人有时不得不切换Node.JS来解决性能问题(通常,在性能密集型情况下,闭包有点慢)。

为什么?因为该

.bind
算法比使用另一个函数包装一个函数并使用
.call
或复杂得多
.apply
。(有趣的是,它还会返回一个toString设置为[nativefunction]的函数)。

规范的角度和从实现的角度来看,有两种方法可以查看。让我们观察两者。

首先,让我们看一下规范中定义的绑定算法:
  1. 令Target为该值。
  2. 如果IsCallable(Target)为false,则抛出TypeError异常。
  3. 令A为按顺序在thisArg(arg1,arg2等)之后提供的所有参数值的新内部列表(可能为空)。

(21.用参数“参数”调用F的[[DefineOwnProperty]]内部方法,PropertyDescriptor
{[[Get]]:thrower,[[Set]]:thrower,[[Enumerable]]:false,[[Configurable]
]:false}和false。

(22.返回F。

看起来很复杂,不只是包装。

其次,让我们看看如何在Chrome中实现它。

让我们检查

FunctionBind
一下v8(chromeJavascript引擎)源代码:

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]]内部属性”



欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/zaji/5148746.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-11-17
下一篇 2022-11-17

发表评论

登录后才能评论

评论列表(0条)

保存