关于JS函数的bind

https://friskfly.github.io/2016/03/24/about-function-bind-in-js/

昨天被人问到js的bind的作用是什么?

这个倒还能回答出来,bind 以后返回一个新的函数,这个函数能够坚持通报的this上下文。

接着又问了,那末bind两次差别的上下文会如何?

这个一会儿就蒙了,由于日常平凡也没这么用过,因而最先查一下材料。

首先在浏览器中测试一下。

function test(){
  console.log(this.a)
}
var bind1 = test.bind({a:1}) //第一次 bind
var bind2 = bind1.bind({a:2}) // 第二次 bind
bind1()
bind2()

效果以下

1
1

能够看到第二次bind并没有能再转变this的值。

查一下MDN,Function.prototype.bind() , 并没有诠释bind两次会如何。 然则他供应了一个Polyfill,能够了解下bind的完成。

if (!Function.prototype.bind) {
  Function.prototype.bind = function(oThis) {
    if (typeof this !== 'function') {
      // closest thing possible to the ECMAScript 5
      // internal IsCallable function
      throw new TypeError('Function.prototype.bind - what is trying to be bound is not callable');
    }

    var aArgs   = Array.prototype.slice.call(arguments, 1),
        fToBind = this,
        fNOP    = function() {},
        fBound  = function() {
          return fToBind.apply(this instanceof fNOP
                 ? this
                 : oThis,
                 aArgs.concat(Array.prototype.slice.call(arguments)));
        };

    if (this.prototype) {
      // Function.prototype doesn't have a prototype property
      fNOP.prototype = this.prototype;
    }
    fBound.prototype = new fNOP();

    return fBound;
  };
}

能够看到MDN供应的polyfill的完成是oThis做为参数传进来,返回一个新的函数,这个时刻函数是个闭包,依然能够接见oThis变量,然后挪用call/apply来完成指定的上下文。 这类情况下,假如bind两次,相当于闭包套闭包,不论套几层,值都是第一次保留的this值。 即上面polyfill的 oThis 变量。

光看polyfill是不够了,由于并不知道polyfill完成是否是范例。所以照样要看下范例。这里我们参考下 ES2015文档

能够直接看到 19.2.3.2 节 NOTE 2,If Target is an arrow function or a bound function then the thisArg passed to this method will not be used by subsequent calls to F. 假如挪用bind的是一个箭头函数或者是已bind过的函数(bound function),那末再次bind是不会起作用的。 能够看到范例已定义了如许的行动发生的效果,我们能够直接记着这个结论。

然则这里值得注意的是,我们看到范例定义的bind操纵 和 MDN 上供应的polyfill并不一致。polyfill没有完整完成ES2015划定的bind。

比方ES2015 划定了 bound function 的length 和 name 行动。

Let targetHasLength be HasOwnProperty(Target, "length").
ReturnIfAbrupt(targetHasLength).
If targetHasLength is true, then
  Let targetLen be Get(Target, "length").
  ReturnIfAbrupt(targetLen).
  If Type(targetLen) is not Number, let L be 0.
  Else,
    Let targetLen be ToInteger(targetLen).
      Let L be the larger of 0 and the result of targetLen minus the number of elements of args.
Else let L be 0.

这里会划定bound function 的 length 属性,应当和bind之前的length一致。

再看name 的行动

Let targetName be Get(Target, "name").
ReturnIfAbrupt(targetName).
If Type(targetName) is not String, let targetName be the empty string.
Perform SetFunctionName(F, targetName, "bound").

这里划定bound function 的name 应当走 SetFunctionName 要领,而这里SetFunctionName以后的返回值应当是 bound字符串 + 空格 + 本来函数的name

......疏忽了一些形貌
prefix即bound 字符串
If prefix was passed, then
  Let name be the concatenation of prefix, code unit 0x0020 (SPACE), and name.

function a(){}
var b = a.bind()
console.log(b.name)

效果应当是

bound a

而 MDN 的 polyfill 是没有完成这些细节的,所以用bind的时刻假如依赖于这些,是要注意的。

    原文作者:friskfly
    原文地址: https://segmentfault.com/a/1190000004681500
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞