Подъем JavaScript: функция может ссылаться на другую функцию, объявленную под ней?

#javascript #function #scope #hoisting #lexical

#javascript #функция #область видимости #подъем #Лексический

Вопрос:

У меня есть:

 // Shouldn't we have the following hoisted?
// var multiply; (undefined)
// var add; (undefined)

var multiply = function(num) {
  return add(num) * 2;
};

var add = function(num) {
  return num   1;
};

console.log(multiply(1)); // No error, somehow "multiply" calls "add"!
  

Я думал, что переменные JavaScript поднимаются наверх, но не значения, которым они присвоены. Каким-то образом multiply вызов add не возвращает ошибку, даже несмотря на то, что add она объявлена ниже multiply .

Комментарии:

1. Я так думаю, основываясь на моей, по общему признанию, ошибочной памяти о чтении этого blog.bitsrc.io / … , которая может научить вас большему, чем вы хотели знать. Основы, которые я помню, включают в себя то, что каждая функция получает свой собственный контекст выполнения, который включает в себя пару внутренних лексических сред (одна из которых специфична для тех идентификаторов, объявленных с помощью var ), и эти лексические среды могут обращаться к внешней среде. Кроме того, я думаю, что объявления var and function поднимаются, а объявления let and const — нет…

2. Объяснение довольно простое. JS позволяет вашим функциям ссылаться на переменные, которые не существуют. Таким образом, вероятность ошибки возникает только после вызова функции. Во время вызова, если указанная переменная не существует, вы получаете сообщение об ошибке. В качестве теста удалите add() функцию и обновите multiply() , чтобы она использовала try/catch , где try пытается вызвать add() , и catch создает ее в window.add = function() {...} . Вы увидите, что ошибка ReferenceError обнаруживается при первом вызове, но последующие вызовы выполняются успешно.

3. …вот так: jsfiddle.net/cpzj309w

Ответ №1:

Вы правы в том, что объявления поднимаются наверх, а назначения — нет.

Однако функции не сохраняют значения каких-либо переменных за пределами них с момента их создания. Вместо этого они используют все, что в них есть, когда они вызываются. В этом случае, add это undefined когда multiply функция создана, но ей назначена функция перед multiply вызовом, поэтому multiply используется новая назначенная функция.

Чтобы увидеть это более четко, рассмотрим этот код:

 var multiply = function(num) {
    return add(num) * 2;
};

// Would be an error
// console.log(multiply(1));

var add = function(num) {
    return num   1;
};

console.log(multiply(1)); // Prints 4

add = function(num) {
    return num   2;
};

console.log(multiply(1)); // Prints 6  

Последняя console.log выводится 6 , потому что multiply использовал новую функцию в add вместо того, чтобы сохранить ту, которая была у него раньше.