Можете ли вы восстановить функцию отскока, используя `bind`

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

Ясно, что я не могу повторить попытку, функция уже отскочила. Однако я не мог найти никакой документации по этому поведению.

Цитата из "Привязать больше аргументов уже связанной функции в Javascript"

Как только вы привязали объект к функции со связыванием, вы не можете ее переопределить. Он четко написан в спецификациях, как вы можете видеть в документации MDN:

Функция bind() создает новую функцию (связанную функцию) с тем же самым телом функции (внутреннее свойство вызова в терминах ECMAScript 5) в качестве функции, на которую она вызывается (функция целевой функции привязки), с этим значением связанный с первым аргументом bind(), который нельзя переопределить.

Я не мог найти их в документации MDN. Я сделал точный полнотекстовый поиск по приведенной выше цитате в Google и кажется, что ответ SO выше - единственный источник такого поведения. Я также пытаюсь найти ответ в спецификации языка без везения.

Мой вопрос: знаете ли вы это поведение и где я могу найти официальную документацию по этим вопросам?

Ответ 1

Это не может напрямую ответить на вопрос о получении официально задокументированной спецификации, подтверждающей это поведение, но мы можем основывать наши выводы на исходном коде, представленном в MDN, в частности, в документации для Function.prototype.bind(), в разделе Polyfill, где они приводят пример того, как a 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, предоставляемой как параметр в исходном вызове bind.

Таким образом, не имеет значения, сколько раз вы повторно привязываете функцию bound fBound, эта функция уже привязана навсегда к исходному контексту oThis в пределах его закрытия.

Документация MDN также указывает на приложение привязки Raynos для дальнейшего использования, что также соответствует этому примеру.

Ответ 2

Проблема в том, что Function.prototype.bind возвращает новую функцию вместо нее. Вызов связанной функции с другим этим аргументом не имеет никакого эффекта, потому что связанная функция уже знает, какое значение использовать в качестве этого аргумента.

Вы можете использовать это для привязки своих функций:

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

Я надеюсь, что это может вам помочь;)

Ответ 3

Метод привязки обертывает исходную функцию и создает новую ограниченную функцию. Фактически, функция, которая обертывает исходную функцию, сохраняя одно и то же тело исходной функции. Это определение на веб-сайте 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();