Вопрос:

Цепочка «связать» и «позвонить» в JavaScript?

javascript chaining

1016 просмотра

3 ответа

67 Репутация автора

Когда я читаю этот ответ , найду var g = f.call.bind(f);. Я не могу понять это своим первым взглядом.

Так имеет ли он какое-то прямое значение, и имеет ли некоторые подходящие сценарии использования?

И далее, когда вы используете call(or apply)или bindили оба в цепочке, что произойдет? Есть ли какие-то законы?

Автор: 2hu Источник Размещён: 03.07.2015 04:30

Ответы (3)


3 плюса

414515 Репутация автора

var g = f.call.bind(f);, Я не могу понять это своим первым взглядом.

Я предполагаю , что вы познакомились с как .call()и методами? Ну, это связывает метод с функцией ..bind() Functionf.callf

Обратите внимание, что f.callэто просто Function.prototype.call, не имеет значения, что мы обращаемся к нему как к свойству, fпотому что мы не называем его здесь.

Так имеет ли это какое-то прямое значение?

Это может стать более очевидным, когда мы посмотрим на эквивалент ES6:

(Function.prototype.call).bind(f) // or
(f.call).bind(f) // is basically
(...args) => f.call(...args) // or more clear
(ctx, ...args) => f.call(ctx, ...args)

Есть ли у него подходящие сценарии использования?

Ну, теперь, когда вы знаете, что он делает, да. Он может взять метод-прототип и преобразовать его в статическую функцию, которая принимает экземпляр в качестве первого аргумента. Пример:

function Example(n) { this.name = n; }
Example.prototype.display = function() { console.log(this.name); }

Example.display = Function.call.bind(Example.prototype.display);

var e = new Example;
e.display(); // is the same as
Example.display(e);

Есть ли какие-то законы для дальнейшей цепочки call/ apply/ bind?

Да: как всегда, только последнее свойство в цепочке фактически вызывается как метод. В приведенном выше примере, Тереза нет разницы между f.call.bind(…), Function.prototype.call.bind(…)или Function.call.apply.bind.call.bind(…)- это всегда вызывает bindна call.

Однако, передавая их в качестве аргументов друг другу, вы можете делать некоторые сумасшедшие вещи, которые более или менее полезны.

Автор: Bergi Размещён: 03.07.2015 08:03

1 плюс

0 Репутация автора

Хороший вопрос. Давайте начнем с рассмотрения примера, который уже был создан в StackOverflow: отображение всех строк в массиве в нижний регистр. Конечно могу написать

strings . map(function(string) { return string.toLowerCase(); })

но это кажется немного многословным. Я бы лучше написал

strings . map(CALL_LOWERCASE_WITH_ELT_AS_THIS)

Так что я могу попробовать

strings . map(String.prototype.toLowerCase)

или, чтобы использовать более короткую идиому некоторые предпочитают

strings . map(''.toLowerCase)

потому что ''.toLowerCaseв точности равно String.prototype.toLowerCase.

Но это, конечно, не сработает, потому что mapкаждый элемент передается указанной функции в качестве первого аргумента , а не его this. Поэтому нам нужно как-то указать функцию, первый аргумент которой используется для вызова какой-либо другой функции в качестве своей this. Это, конечно, именно то, что Function.callделает:

function.call(context)

Первый аргумент call(«context») используется как thisпри вызове function.

Итак, проблема решена? Мы должны просто сказать:

strings . map(''.toLowerCase.call)

и люди попробовали это, а потом удивляются, почему это не сработало. Причина в том, что, хотя мы передаем callв toLowerCaseкачестве обратного вызова map, mapвсе еще не понимаем, что обратный вызов должен вызываться с помощью thisof ''.toLowerCase. Нам нужно явно указать, mapкакой из них thisиспользовать для вызова функции, что в случае mapмы можем сделать со вторым аргументом «context»:

strings . map(''.toLowerCase.call, ''.toLowerCase)

На самом деле, поскольку callто же самое на любом функциональном объекте, мы можем упростить это до

strings . map(Function.call, ''.toLowerCase)

Это работает и делает работу красиво.

Однако, хотя mapэтот второй аргумент «контекста» указывает thisдля вызова обратного вызова, это не то, от чего мы можем зависеть, чтобы быть доступными во всех ситуациях. Нам нужен более общий способ сказать «создать функцию, которая вызывает Function.callкакую-то конкретную функцию как this».

Это именно то, что bindделает. Он говорит «возьми функцию и сделай другую функцию, которая вызывает ее с определенной this»:

function.bind(context)

В нашем случае мы хотим «взять функцию Function.callи создать другую функцию, которая вызывает ее с помощью thisof ''.toLowerCase. Это просто

Function.call.bind(''.toLowerCase)

Теперь мы можем передать это mapбез использования второго аргумента:

strings . map(Function.call.bind(''.toLowerCase))

Это работает точно так же, как strings . map(Function.call, ''.toLowerCase), потому что в целом map(fn, ctxt)точно равно map(fn.bind(ctxt)).

Следующее разбивает это на читаемую форму, шаг за шагом:

Function .           // From the Function object
  call .             // take the `call` method
  bind(              // and make a new function which calls it with a 'this' of
    ''.toLowerCase   // `toLowerCase`
  )

Когда эта конструкция указана как обратный вызов, например map, это означает:

Вызывать callс первым переданным аргументом и ''.toLowerCaseкак this, что в силу определения call, означает вызывать toLowerCaseс этим аргументом как this.

Некоторые люди предпочитают немного упростить это, сказав

var call = Function.call;
var toLowerCase = ''.toLowerCase;

strings . map(call.bind(toLowerCase))

или, используя второй аргумент, предоставленный map, просто

strings . map(call, toLowerCase)

который почти читаемый как английский: " карта каждой строки в результате вызова ИНГ toLowerCase .

Другим распространенным случаем использования может быть указание обратного вызова в thenобещании. Рассмотрим следующий код:

promise . then(function(result) { result.frombulate(); })

Это хорошо, но это немного многословно. И не thenимеет никакого способа передать в контексте, который будет использоваться, как thisпри вызове обработчика успеха или сбоя. Но с учетом вышесказанного мы можем теперь написать:

promise . then(call.bind(frombulate))

Существуют и другие варианты использования этой call.bindидиомы, но это один из наиболее распространенных: определить обратный вызов, результатом которого является вызов некоторой функции с параметром, переданным обратному вызову в качестве ее this.

С ES6 жирными стрелками функций, конечно, я могу написать

promise . then(result => result.frombulate())

таким образом, в сокращении, предлагаемом относительно, есть относительно меньшее преимущество call.bind(frombulate), и трудно отрицать, что версия с жирной стрелкой более читаема, чем версия, используемая bind.

Также может быть интересен следующий вопрос: Array.map и поднятые функции в Javascript .

Автор: user663031 Размещён: 03.07.2015 10:13

-1 плюса

881 Репутация автора

m.call.bind(m)

может использоваться как сокращение для:

function(x){return m.bind(x)()}

Первый является «бессмысленной» формой второго, аргументы неявны. Это было бы полезно для операций со списками, таких как map (), делая их короче. Вы можете написать что-то вроде:

let m   = "".toUpperCase;
let fun = m.call.bind(m);
let see = ['a','b'].map(fun);
Автор: Panu Logic Размещён: 07.09.2019 12:07
Вопросы из категории :
32x32