Вызов и привязка функции Javascript

У меня есть следующая часть кода:

var person = {
  name: "Brendan Eich",
  hello: function(thing) {
    console.log(this.name + " says hello " + thing);
  }
}

var bind = function(func, thisValue) {
  return function() {
    return func.apply(thisValue, arguments);
  }
}

var boundHello = bind(person.hello, person);
boundHello("world") // "Brendan Eich says hello world"

Здесь код выведет в консоль текст

Брендан Эйх говорит привет, мир

если я возьму назначение переменной связывания и изменю его на:

var person = {
  name: "Brendan Eich",
  hello: function(thing) {
    console.log(this.name + " says hello " + thing);
  }
}

var bind = function(func, thisValue) {
  return func.apply(thisValue, arguments);
}

var boundHello = bind(person.hello, person);
boundHello("world") // "Brendan Eich says hello world"

тогда результаты

Брендан Эйх передает привет function(thing) { console.log(this.name + "привет" + thing); }

Может ли кто-нибудь объяснить мне, почему это происходит и что такое две вложенные функции return внутри функции bind?

Как именно они работают?


person Efthymios Kalyviotis    schedule 20.02.2019    source источник
comment
Когда я запускаю ваш второй пример, я получаю uncaught TypeError: boundHello is not a function - это то, что я ожидал (но все равно проверял его на случай, если я что-то пропустил). Разница в том, что первая (правильная) версия bind возвращает функцию — версию func, которая всегда связана с thisValue в качестве ссылки на this. Вторая (неправильная) версия просто сразу возвращает (нефункциональное) значение (в данном случае это undefined, но в целом это будет то, что func возвращает для аргументов, которые вы предоставили), поэтому вы не можете вызвать это.   -  person Robin Zigmond    schedule 21.02.2019
comment
Я думаю, что немного понимаю, что вы подразумеваете под нефункцией.   -  person Efthymios Kalyviotis    schedule 21.02.2019


Ответы (2)


Я постараюсь объяснить как можно лучше. В первом примере ваше определение bind — это function, которое получает два аргумента, в идеале — функцию в качестве первого аргумента (давайте назовем ее THE_FUNC) и object в качестве второго аргумента (назовем его THE_THIS_ARG), который будет использоваться как thisArgs для этой функции.

Теперь это определение bind возвращает function, которое явно не принимает аргументов (но неявно может принимать любые аргументы), и когда эта функция вызывается, она возвращает оценку вызова применить к THE_FUNC, используя THE_THIS_ARG в качестве значения this для THE_FUNC и использования полученного arguments (давайте назовем его THE_ARGS) в качестве аргументов, передаваемых в THE_FUNC.

Для вашего конкретного примера настройка будет такой:

THE_FUNC => person.hello

THE_THIS_ARG => person

THE_ARGS => ["world"] (объект, подобный массиву)

Наконец, boundHello("word") затем трактуется примерно так (не совсем так):

person.hello.apply(person, ["world"]);

Возможно, следующий пример с некоторыми отладками поможет вам понять лучше, чем мое объяснение:

var person = {
    name: "Brendan Eich",
    hello: function(thing)
    {
        console.log(this.name + " says hello " + thing);
    }
}

var bind = function(func, thisValue)
{
    console.log("func is: ", func);
    console.log("thisValue is: ", thisValue);
    return function()
    {
        console.log("Arguments are:", arguments);
        return func.apply(thisValue, arguments);
    }
}

var boundHello = bind(person.hello, person);
boundHello("world") // "Brendan Eich says hello world"

То же самое объяснение справедливо и для второго примера. Но в этом случае bind не возвращает функцию, как в предыдущем примере, а вместо этого вызывает apply с такой настройкой:

THE_FUNC => person.hello

THE_THIS_ARG => person

THE_ARGS => [person.hello, person] (объект, подобный массиву)

Итак, когда вы вызываете bind(person.hello, person), это превращается во что-то вроде:

person.hello.apply(person, [person.hello, person]);

И, как вы можете видеть, person.hello будет аргументом thing, полученным методом person.hello(), и именно поэтому определение функции печатается вместо аргумента thing.

var person = {
    name: "Brendan Eich",
    hello: function(thing)
    {
        console.log(this.name + " says hello " + thing);
    }
}
    
var bind = function(func, thisValue)
{
    console.log("Arguments are:", arguments);
    return func.apply(thisValue, arguments);
}

var boundHello = bind(person.hello, person);
//boundHello("world") // "Uncaught TypeError: boundHello is not a function"

person Shidersz    schedule 20.02.2019
comment
Спасибо! Ведение журнала консоли, похоже, помогло мне понять! - person Efthymios Kalyviotis; 21.02.2019

Во второй версии вы вызываете func при вызове bind() и возвращаете значение вызова функции.

Он печатает исходный код функции hello, потому что arguments является массивоподобным объектом [person.hello, person], поэтому вы фактически выполняете вызов person.hello(person.hello, person), и это устанавливает thing в person.hello, а преобразование функции в строку с целью конкатенации возвращает ее значение. исходный код.

Чтобы связать функцию, вы должны вернуть замыкание, которое можно будет вызвать позже. Это делается в вашей первой версии, возвращая анонимную функцию.

person Barmar    schedule 20.02.2019
comment
Меня сбивает с толку то, что возвращаемая функция должна иметь параметр, который сейчас она не принимает. Верно? Я также предполагаю, что мне следует прочитать о закрытии Javascript...? - person Efthymios Kalyviotis; 21.02.2019
comment
Возвращаемая функция использует специальную переменную arguments, которая автоматически устанавливается в массив всех аргументов, переданных функции. Так что он будет работать с любым количеством параметров. - person Barmar; 21.02.2019