Связывание .apply () и .bind () удивительного поведения

Мне нужна ваша помощь, чтобы лучше понять следующее поведение.

В приведенном ниже фрагменте мы видим, что fooBar выводит foo как this, а затем returns bar 1, 2, 3, как и ожидалось - это означает, что bar вызывается с foo в качестве контекста.

const arguments = [1,2,3];

function bar(...args) {
  console.info('bar this ->', this);
  return 'bar ' + args;
}

function foo(...args) {
  console.info('foo this ->', this);
  return 'foo ' + args;
}

const fooBar = bar.bind(foo, null)(arguments);

console.info(fooBar); // <--- no surprises!

Вместо этого возьмем const bar = Math.max.apply;.

const arguments = [1,2,3];

const bar = Math.max.apply;

function foo(...args) {
  console.info('foo this ->', this);
  return 'foo ' + args;
}

const fooBar = bar.bind(foo, null)(arguments);

console.info(fooBar); // <--- surprise!

В этом случае вызывается foo, а не bar. Почему? Что именно bind() делает в этом случае под капотом? Я бы предположил, что bar снова следует вызывать с foo в качестве контекста. В данном случае контекст - window.

Я всегда думал someFunction.apply (someContext, аргументы)ведет себя какsomeFunction.bind (someContext, нуль) (аргументы), но во втором примере someFunction.bind (someContext, нуль) (аргументы)ведет себя какsomeContext (аргументы).

🤔 А знаете ли вы, что...
JavaScript позволяет создавать расширения для веб-браузеров, улучшая их функциональность.


3
100
3

Ответы:

bind создает новую функцию, которая вызывает ее значение this (bar, которое является копией apply) со значением, которое вы передаете ей в качестве нового значения this.

Поскольку bar является копией apply:

bar.bind(foo)() такой же, как foo.bar(), такой же, как foo.apply().


Решено

Это связано с конкретной целью apply: вызывать заданную функцию. Помните, что bar - это общая функция Function.prototype.apply.

bind по сути создает копию исходной функции с предустановленным контекстом (значение this) и (необязательно) аргументами. Полифилл для bind будет использовать apply внутри.

Итак, fooBar = bar.bind(foo, null) такой же, как

function fooBar(...args) {
    return Function.prototype.apply.apply(foo, [null, args]);
}

Очевидно, что двойное использование apply сбивает с толку!

Давайте рассмотрим, что будет делать bar.bind(foo, null)(arguments):

Function.prototype.apply.bind(foo, null)(arguments)

который можно свести к

Function.prototype.apply.apply(foo, [null, arguments])

который в данном конкретном случае совпадает с

foo(null, ...arguments);

Это сбивает с толку, потому что вы выполняете сложный вызов функции apply, которая предназначена для сложных вызовов функций!


Давайте сделаем наш собственный "импровизированный" myApply, который имитирует собственный Function#apply, чтобы лучше понять, как работает Function#apply.

Function.prototype.myApply = function(args) {
    // some checks should be here to see if 'this' is a function and if 'args' is an array-like
    this(...args);
}

Он принимает массив (или подобный массиву объект), содержащий аргументы, и вызывает this (независимо от того, к чему применяется myApply), которая должна быть функцией, в которой каждый элемент из args передается как отдельный аргумент.

Простой пример:

Теперь, когда вы это сделаете:

alert.myApply(["hello, there"]);

alert передается как this в myApply (не требует пояснений), а ["hello, there"] передается как args, и все работает должным образом.

Непонятный пример:

Теперь, когда вы явно устанавливаете this для myApply, используя bind, call или даже apply, вот так:

var myApply = alert.myApply;

var newFunction = myApply.bind(foo);

newFunciton становится новой функцией (функцией, которая эквивалентна myApply, связанному с foo). Вызов newFunction аналогичен вызову myApply с его this, установленным на foo. Когда вызывается myApply, вызывается его значение this (в данном случае это foo).

Дополнительное примечание: Как вы можете видеть, myApply не вмешивается в контекст своего this (который является функцией), поэтому эта функция будет вызываться с любым контекстом, в котором она была до вызова myApply, в этом конкретном примере это глобальный объект window ( foothis - это window).