你能用`bind`重新绑定一个反弹函数吗?

时间:2017-04-26 14:56:37

标签: javascript

bind方法创建一个新函数,在调用时将this关键字设置为提供的值。

var obj = {
  a: 0,
  b() {
    console.log(this.a);
  }
}

obj.b() // -> 0

var functionBound = obj.b.bind(obj)
functionBound() // -> 0
functionBound.bind(null)() // -> 0 AND I expect an error here
显然,我无法重新绑定一个已经被反弹的功能。但是,我找不到有关此行为的任何文档。

引自“Bind more arguments of an already bound function in Javascript

  

将对象绑定到具有bind的函数后,您无法覆盖它。正如你在MDN文档中看到的那样,它清楚地写在规范中:

     

bind()函数创建一个新函数(一个绑定函数),它具有相同的函数体(ECMAScript 5术语中的内部调用属性),因为它被调用的函数(绑定函数的目标函数)具有此值绑定到bind()的第一个参数,它不能被覆盖。

我在MDN文档中找不到这些内容。我在Google上面的引文上做了一个精确的全文搜索,似乎上面的SO答案是这种行为的唯一来源。我也试着在语言规范中找到一个没有运气的答案。

我的问题是你知道这种行为吗?我在哪里可以找到关于这些行为的官方文件?

3 个答案:

答案 0 :(得分:4)

这可能无法直接回答有关获得正式记录的规范验证此行为的问题,但我们的结论可以基于MDN中提供的源代码,特别是Function.prototype.bind()的文档,在{{3它们提供了一个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;
  };
}

我们可以看到oThis参数用在闭包fBound中,这是bind函数最终返回的那个。

这意味着当你调用bind函数时,你得到一个闭包函数,当被调用时,它会访问oThis自由变量作为{{1}原始调用中的参数提供的}。

因此,重新绑定绑定bind函数多少次并不重要,此函数已经永久绑定到其闭包中的原始上下文fBound

MDN文档还指向Polyfill以供进一步参考,这似乎也与此示例相对应。

答案 1 :(得分:1)

问题是Function.prototype.bind返回一个NEW函数而不是相同的函数。使用不同的this-argument调用绑定函数无效,因为绑定函数已经知道要将哪个值用作this-argument。

您可以使用它来绑定您的功能:

Function.boundOriginProp = Symbol()
Function.prototype.bindDynamic = thisArg => {
    let origin = this[Function.bindOriginProp] || this
    let bound = (...args) => origin.call(thisArg, ...args)
    bound[Function.bindOriginProp] = origin
    return bound
}

所以你可以重新绑定已经绑定的函数:

let obj1 = { value: 1 }
let obj2 = { value: 2 }

function example() {
    console.log(this.value)
}

let fn1 = example.bindDynamic(obj1)
fn1() // -> 1

let fn2 = fn1.bindDynamic(obj2)
fn2() // -> 2

let fn3 = fn1.bindDynamic(null)
fn3() // -> undefined

我希望这可以帮到你;)

答案 2 :(得分:0)

bind方法包装原始函数并创建一个新的Bounded函数。 实际上,包含原始函数的函数保持原始函数的相同主体。 这是MDN网站上的定义:

  

bind()函数创建一个新的绑定函数(BF)。 BF是一个   外来的功能对象(来自ECMAScript 2015的术语)包装了   原始功能对象。调用BF通常会导致   执行包装函数。

因此,每次调用.bind时,都会创建一个新函数,将上下文作为第一个参数传递,将args作为其余参数传递,但保留第一个定义的主体。 您还可以覆盖初始正文,然后再次绑定该函数。 同时,您还可以使用先前有界的函数并将其再次绑定到新函数 在以下示例中,您应该看到预期的行为:

var printer = function(a) { 
    console.log(a); 
};

var printer1 = printer.bind(null, 1);
var printer2 = printer.bind(null, 2);
var printer3 = printer.bind(null, 3);

printer1();
printer2();
printer3();

printer = function(a) {
    console.log("I am a new " + a); 
};

var printer4 = printer.bind(null, 4);
printer4();

var newPrinter = function() {
    console.log('I am a new printer!');
}
printer4 = newPrinter.bind(null);
printer4();