#javascript #promise #ecmascript-6 #bind #apply
#javascript #обещание #ecmascript-6 #привязка #применить
Вопрос:
Итак, я понимаю, что из обещания возвращается массив [200,599], а функция обратного вызова внутри spread передается в Function.apply.bind, но теперь я потерялся. Как массив [200,599] разбивается на x и y? Как именно работает apply.bind?
function getY(x) {
return new Promise( function(resolve,reject){
setTimeout( function(){
resolve( (3 * x) - 1 );
}, 100 );
} );
}
function foo(bar,baz) {
var x = bar * baz;
// return both promises
return [
Promise.resolve( x ),
getY( x )
];
}
function spread(fn) {
return Function.apply.bind( fn, null );
}
Promise.all(
foo( 10, 20 )
)
.then(
spread( function(x,y){
console.log( x, y ); // 200 599
} )
)
Комментарии:
1. Вы знаете, что они делают сами по себе, верно?
Ответ №1:
.apply()
это метод для объектов функций. Вот так:
console.log(Math.max.apply(null, [1, 2, 3])); // 3
.apply()
принимает два аргумента, контекст (то, что станет this
внутри целевой функции) и итерируемый набор аргументов (обычно массив, но arguments
подобный массив также работает).
.bind()
это метод для объектов функций. Вот так:
const x = {
foo: function() {
console.log(this.x);
},
x: 42
}
var myFoo = x.foo.bind({x: 5});
x.foo(); // 42
myFoo(); // 5
.bind()
принимает контекст (который станет this
) и, при необходимости, дополнительные аргументы и возвращает новую функцию с привязкой к контексту и блокировкой дополнительных аргументов
Поскольку .apply()
это функция сама по себе, ее можно связать с .bind()
, например, так:
Function.prototype.apply.bind(fn, null);
Это означает, что this
of .apply()
будет fn
и первый аргумент to .apply()
будет null
. Это означает, что это будет выглядеть так:
fn.apply(null, args)
Который распределил бы параметры из массива.
Ответ №2:
Spread принимает функцию и связывает apply
метод с, частично применяя null
аргумент. Итак, вкратце,
spread(fn)
преобразуется в
args => fn.apply(null, args)
что аналогично использованию синтаксиса распространения ES6
args => fn(...args)
откуда функция получила свое имя.
Если вам нужен длинный ответ, помните, что bind
делает:
method.bind(context, ...args1)
возвращает функцию, которая работает как
(...args2) => method.call(context, ...args1, ...args2)
В нашем случае method
is apply
, context
is fn
и первые аргументы являются null
, поэтому вызов
Function.apply.bind( fn, null )
создаст функцию, которая работает как
(...args2) => (Function.apply).call(fn, null, ...args2)
что эквивалентно fn.apply(…)
приведенному выше вызову, учитывая, что apply
это метод, унаследованный от Function.prototype
обоих обращений.
Комментарии:
1. Хорошее объяснение, я не понял, что это исправляет первый
apply
аргументnull
, но зачем нам это нужно, есть ли какие-либо реальные полезные ситуации, в которых это может быть полезно!!2. @SaherElgendy Не уверен, о чем ты спрашиваешь.
apply
нужен первый аргумент, а что еще вы бы передали? Если вы спрашиваете о полезностиspread
функции, это в основном устарело, поскольку синтаксис ES6 rest / spread .3. Вы имеете в виду, что эта часть
Function.apply.bind( fn, null )
— просто старый способ выражения синтаксиса распространения?4. @SaherElgendy
spread
— это функция более высокого порядка, в основном декоратор, вокруг вызываемой функции. Вместо написанияspread(function(x,y) { … })
мы бы просто использовалиfunction([x, y]) { … }
.5. Конечно, но
spread(function(x,y) { … })
гораздо более «практично», чемfunction(arr) { var x = arr[0], y = arr[1]; … }
.
Ответ №3:
Функция распространения — это просто служебная функция для преобразования массива в параметры, передаваемые функции. Приложение выполняет преобразование, а привязка привязывает его к вызывающей функции, чтобы контекст «this» был подключен к той же функции.
Чтобы увидеть, как работает spread в более простой форме ->
spread(function (x,y){console.log(x,y);})([1,2])
Вы получите ответ, 1 2, поскольку 1 передается в x, а 2 передается в y .
Итак, в вашем примере ваше обещание.все возвращает массив разрешенных обещаний. Затем они сопоставляются с параметрами вашей функции (x, y).
Ответ №4:
Причина, по которой это работает, — это «деструктурирующий» характер apply
(если задан массив значений, они будут предоставлены для функции, к которой вы используете apply).
Теперь вернемся к вашему коду при вызове bind в apply возвращаемое значение представляет собой функцию, которая возвращает ту же функцию, что и предоставленная bind
, единственное отличие в том, что при выполнении она будет вызываться с использованием apply
(с массивом как thisArg в вашем случае), но она не будет выполнена, пока вы ее не вызовете. В вашем случае, когда обещание будет выполнено, функция, предоставленная then
вам, будет выполнена с массивом аргументов, предоставленных Promise
resolution .
function spread(fn){
let boundedFn = fn.bind(fn)
return function(arg){
return boundedFn.apply(null,arg)
}
}
spread((x, y, c) => console.log(x, y, c))([1,2,3])
// or
Promise.resolve([6, 5]).then(spread((a, b) => console.log(a, b)))
Причина bind
, указанная (в вашем коде) в null
качестве второго параметра, заключается в том, чтобы предотвратить передачу массива, предоставленного вызывающим объектом, в apply
качестве его первого параметра, который зарезервирован для this
.