Как function.apply.bind работает в следующем коде?

#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 .