двойной bind задачка

217
16 марта 2018, 15:31

Может кто нить объяснить, почему в итоге выводится информация из первого bind, а не последнего?

function f() { 
  console.log(this.name); 
} 
 
f = f.bind({name: "Вася"}).bind({name: "Петя"}); 
 
f(); // Вася

Answer 1

Для начала заменим стандартный метод bind на свою функцию, которая делает все то же самое. Связывает контекст

function myBind(func, thisArg) { 
  return (function() { 
    func.apply(thisArg); 
  }); 
} 
 
function f() { 
  console.log(this.name); 
} 
 
f = myBind(myBind(f, {name: "Вася"}), {name: "Петя"}); 
 
// Эквивалентно следующим двум строкам 
//f = myBind(f, {name: "Вася"}) 
//f = myBind(f, {name: "Петя"}); 
 
f();

А теперь в нашу функцию myBind добавим логирование

function objToStr(obj) { 
  if (obj == window) 
    return "window"; 
  else 
    return JSON.stringify(obj); 
} 
 
function myBind(func, thisArg, desc) { 
  console.log("Bind: Desc: " + desc + ", thisArg: " + objToStr(thisArg) + ", this:" + objToStr(this)); 
  return (function() { 
    console.log("Call: Desc: " + desc + ", thisArg: " + objToStr(thisArg) + ", this:" + objToStr(this)); 
    func.apply(thisArg); 
  }); 
} 
 
function f() { 
  console.log(this.name); 
} 
 
f = myBind(f, {name: "Вася"}, "first") 
f = myBind(f, {name: "Петя"}, "second"); 
 
console.log("After Bind"); 
 
f();

Т.е. в итоге наш вызов f() последовательно вызывает две обертки и в конце - нашу функцию

Answer 2

И немного спецификации. Функция bind возвращает так называемый exotic object - похожий на функцию, но не функцию.

У этого объекта есть три внутренних поля:

  1. [[BoundTargetFunction]] - ссылка на функцию, у которой вызвали bind
  2. [[BoundThis]] - значение this, которое будет передаваться при вызове функции
  3. [[BoundArguments]] - список значений, которые будут использованы как первые параметры при вызове функции

Кроме того, у данного объекта переопределен метод внутренний метод Call. Он работает следующим образом:

  1. К списку аргументов сохраненных в поле [[BoundArguments]] добавляются аргументы переданные при вызове
  2. Вызов функции сохраненной в [[BoundTargetFunction]] с передачей в качестве this значения поля [[BoundThis]] и обновленного списка аргументов.

Таким образом для кода

function f() {...}
var firstBind = f.bind({name: "Вася"})
var secondBind = fisrtBind.bind({name: "Петя"});
secondBind(); // Вася

вызов secondBind() равносилен вызову firstBind.call({name: "Петя"}), что, в свою очередь, равносильно вызову f.call({name: "Вася"}) так как при вызове экзотического объекта this для вызываемой функции устанавливается непосредственно из его внутреннего поля.

Answer 3

Ответ (объяснение) же уже дан(о)! Вот здесь (или, если ближе к вопросу, то здесь).

Процитирую главное:

Первый вызов f.bind(..Вася..) возвращает «обёртку», которая устанавливает контекст для f и передаёт вызов f.

Следующий вызов bind будет устанавливать контекст уже для этой обёртки. Это ни на что не повлияет.

READ ALSO
Ограничение на количество итераций

Ограничение на количество итераций

этот код отвечает за вывод количества файлов, сейчас он без ограничений может выводить хоть 100 или 200 файлов, но что нужно в нем изменить что...

196
Обновить элемент в ReactJS

Обновить элемент в ReactJS

Как обновить элемент HelloWorld из функции updateHelloWorld и вообще это возможно?

194
Проверка состояния promise в angularjs

Проверка состояния promise в angularjs

Есть сервис который делает запрос через $http , в обработке запроса (неважно resolve или reject) , я возвращаю какие то данные через return, в итоге сервис...

165
Плохо работает баланс скобок JavaScript [дубликат]

Плохо работает баланс скобок JavaScript [дубликат]

На данный вопрос уже ответили:

307