Мне нужна ваша помощь, чтобы лучше понять следующее поведение.
В приведенном ниже фрагменте мы видим, что 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 позволяет создавать расширения для веб-браузеров, улучшая их функциональность.
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
( foo
this
- это window
).