之前的一张海报问Function.bind vs Closure in Javascript : how to choose?

并在一定程度上得到了这个答案,这似乎表明绑定应该比关闭更快:

范围遍历意味着,当你要获取一个值时

使用bind调用一个具有现有作用域的函数

两个jsperf表明bind实际上比closure慢得多.

这是作为对上述内容的 comments 发布的

然后,我决定写my own jsperf

那么,为什么绑定速度要慢得多( chromium 含量为70%?)?

既然不是更快,闭包也可以达到同样的目的,那么应该避免绑定吗?

推荐答案

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 来看,有两种方法.让我们观察两者.

First, let's look at the bind algorithm defined in the specification:

  1. 将目标设为该值的最大值.
  2. 如果IsCallable(Target)为false,则抛出TypeError异常.
  3. A是一个新的(可能是空的)内部列表,其中包含在thisArg(arg1、arg2等)之后提供的所有参数值.

...

(21.使用参数"arguments"、PropertyDescriptor{[[Get]]:Trower、[[Set]]:Trower、[[Enumerable]]:false、[[Configurable]]:false}和false调用F的[[DefineOwnProperty]]内部方法.

(22)返回F.

看起来很复杂,不仅仅是一个Package.

Second , let's see how it's implemented in Chrome.

让我们判断一下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]]内部属性"

Node.js相关问答推荐

如何在Node.js 应用程序中从react js公共目录渲染图像?

如何从puppeteer的page. evaluate()中获取流数据?(node.js)

如何使用NodeJS在mongodb中更新文档

仅在一次查询中 MongoDB 上最近的一对位置

使用更新版本仍然找到包@angular/fire但不支持原理图

node_modules/preact/src/jsx.d.ts:2145:22 - 错误 TS2304:找不到名称SVGSetElement

在 Header 中使用 Authorization 方法和新的附加参数:accessToken 有什么区别?

Fly.io 启动问题:无法从源获取图像或构建错误:构建错误:连接期间出错:发布...

获取用户 ID 后,Firebase 函数 onCreate 方法在 Firestore 上不起作用

无法通过谷歌Electron 表格 api( node js)中的服务帐户访问写入

fs.writefile 选项参数的可能值,尤其是只读文件的整数

突然 React 无法执行create-react-app命令.为什么会发生这种情况,我该如何解决?

file.slim.js 中的苗条是什么

eslint - vscode 的可选链接错误

Base64 编码一个 javascript 对象

node/express:使用 Forever 连续运行脚本时设置 NODE_ENV

如何解决'npm应该在 node repl之外运行,在你的普通shell中'

Node.js 17.0.1 Gatsby 错误-数字信封 routine ::不支持 ... ERR_OSSL_EVP_UNSUPPORTED

如何忽略文件 grunt uglify

FireStore 如果不存在则创建一个文档