В JavaScript функция — это не «магическая языковая структура», а особый вид значения.
Синтаксис, который мы использовали ранее, называется объявлением функции :
функция SayHi() {
Предупреждение("Привет");
}Существует другой синтаксис для создания функции, который называется выражением функции .
Это позволяет нам создавать новую функцию в середине любого выражения.
Например:
пусть говорятПривет = функция () {
Предупреждение("Привет");
}; Здесь мы видим переменную sayHi получающую значение, новую функцию, созданную как function() { alert("Hello"); } .
Поскольку создание функции происходит в контексте выражения присваивания (справа от = ), это выражение функции .
Обратите внимание: после ключевого слова function нет имени. Для функциональных выражений допускается пропуск имени.
Здесь мы сразу присваиваем ее переменной, поэтому смысл этих примеров кода один и тот же: «создать функцию и поместить ее в переменную sayHi ».
В более сложных ситуациях, с которыми мы столкнемся позже, функция может быть создана и немедленно вызвана или запланирована для более позднего выполнения, нигде не сохраняясь и, таким образом, оставаясь анонимной.
Еще раз повторим: независимо от того, как создается функция, функция является значением. В обоих приведенных выше примерах функция сохраняется в переменной sayHi .
Мы даже можем распечатать это значение, используя alert :
функция SayHi() {
Предупреждение("Привет");
}
Оповещение (сказать Привет); // показывает код функции Обратите внимание, что последняя строка не запускает функцию, поскольку после sayHi нет круглых скобок. Существуют языки программирования, в которых любое упоминание имени функции приводит к ее выполнению, но в JavaScript дело обстоит иначе.
В JavaScript функция является значением, поэтому мы можем обращаться с ней как со значением. В приведенном выше коде показано его строковое представление, которое является исходным кодом.
Конечно, функция — это особое значение в том смысле, что мы можем вызывать ее как sayHi() .
Но это все равно ценность. Таким образом, мы можем работать с ним, как и с другими видами ценностей.
Мы можем скопировать функцию в другую переменную:
functionsayHi() { // (1) создать
Предупреждение("Привет");
}
пусть func = SayHi; // (2) копирование
функция(); // Привет // (3) запусти копию (работает)!
сказатьПривет(); // Привет // это тоже работает (почему бы и нет)Вот что происходит подробно выше:
Объявление функции (1) создает функцию и помещает ее в переменную с sayHi .
Строка (2) копирует его в переменную func . Обратите внимание еще раз: после sayHi скобок нет. Если бы они sayHi , то func = sayHi() записал бы в func результат вызоваsayHi sayHi() , а не саму функциюsayHi .
Теперь функцию можно вызывать как sayHi() так и func() .
Мы также могли бы использовать функциональное выражение для sayHi в первой строке:
letsayHi = function() { // (1) создать
Предупреждение("Привет");
};
пусть func = SayHi;
// ...Все будет работать так же.
Почему в конце стоит точка с запятой?
Вы можете задаться вопросом, почему в функциональных выражениях стоит точка с запятой ; в конце, но объявления функций не делают:
функция SayHi() {
// ...
}
пусть говорятПривет = функция () {
// ...
}; Ответ прост: здесь создается функциональное выражение как function(…) {…} внутри оператора присваивания: let sayHi = …; . Точка с запятой ; рекомендуется использовать в конце инструкции, это не часть синтаксиса функции.
Точка с запятой будет использоваться для более простого присваивания, например, let sayHi = 5; , а также для назначения функции.
Давайте рассмотрим дополнительные примеры передачи функций в качестве значений и использования функциональных выражений.
Напишем функцию ask(question, yes, no) с тремя параметрами:
question
Текст вопроса
yes
Функция, которая будет запущена, если ответ «Да»
no
Функция, которая будет запущена, если ответ «Нет»
Функция должна задать question и, в зависимости от ответа пользователя, вызвать yes() или no() :
функция Ask(вопрос, да, нет) {
если (подтвердить(вопрос)) да()
иначе нет();
}
функция шоуОк() {
alert("Вы согласились.");
}
функция showCancel() {
alert("Вы отменили выполнение.");
}
// использование: функции showOk, showCancel передаются в качестве аргументов для запроса
Ask("Вы согласны?", showOk, showCancel); На практике такие функции весьма полезны. Основное различие между реальным ask и приведенным выше примером заключается в том, что реальные функции используют более сложные способы взаимодействия с пользователем, чем простое confirm . В браузере такие функции обычно рисуют красивое окно с вопросом. Но это другая история.
Аргументы showOk и showCancel команды ask называются функциями обратного вызова или просто обратными вызовами .
Идея состоит в том, что мы передаем функцию и ожидаем, что она будет «вызвана обратно» позже, если это необходимо. В нашем случае showOk становится обратным вызовом для ответа «да», а showCancel — для ответа «нет».
Мы можем использовать функциональные выражения для написания эквивалентной, более короткой функции:
функция Ask(вопрос, да, нет) {
если (подтвердить(вопрос)) да()
иначе нет();
}
просить(
"Вы согласны?",
function() { alert("Вы согласились."); },
function() { alert("Вы отменили выполнение."); }
); Здесь функции объявляются прямо внутри вызова ask(...) . У них нет имени, и поэтому их называют анонимными . Такие функции недоступны за пределами ask (поскольку они не присваиваются переменным), но это именно то, что нам нужно.
Такой код появляется в наших скриптах очень естественно, он в духе JavaScript.
Функция — это значение, представляющее «действие».
Обычные значения, такие как строки или числа, представляют данные .
Функцию можно воспринимать как действие .
Мы можем передавать его между переменными и запускать, когда захотим.
Давайте сформулируем ключевые различия между объявлениями функций и выражениями.
Во-первых, синтаксис: как их различать в коде.
Объявление функции: функция, объявленная как отдельный оператор в основном потоке кода:
// Объявление функции
функция sum(a, b) {
вернуть а + б;
} Выражение функции: функция, созданная внутри выражения или внутри другой синтаксической конструкции. Здесь функция создается в правой части «выражения присваивания» = :
// Выражение функции
пусть сумма = функция (а, б) {
вернуть а + б;
};Более тонкая разница заключается в том, что функция создается движком JavaScript.
Функциональное выражение создается, когда его достигает выполнение, и его можно использовать только с этого момента.
Как только поток выполнения перейдет в правую часть присваивания, let sum = function… — вот и все, функция создана и с этого момента ее можно использовать (назначать, вызывать и т. д.).
Объявления функций разные.
Объявление функции может быть вызвано раньше, чем оно определено.
Например, глобальное объявление функции видно во всем скрипте, независимо от того, где оно находится.
Это связано с внутренними алгоритмами. Когда JavaScript готовится к запуску сценария, он сначала ищет в нем глобальные объявления функций и создает функции. Мы можем думать об этом как о «стадии инициализации».
И после того, как все объявления функций обработаны, код выполняется. Таким образом, он имеет доступ к этим функциям.
Например, это работает:
SayHi("Джон"); // Привет, Джон
функция SayHi(имя) {
alert(`Привет, ${name}`);
} Объявление функции sayHi создается, когда JavaScript готовится к запуску сценария, и отображается в нем повсюду.
…Если бы это было функциональное выражение, оно бы не работало:
SayHi("Джон"); // ошибка!
letsayHi = function(name) { // (*) никакой магии больше
alert(`Привет, ${name}`);
}; Функциональные выражения создаются, когда их достигает выполнение. Это произойдет только в строке (*) . Слишком поздно.
Еще одной особенностью объявлений функций является их область действия блока.
В строгом режиме, когда объявление функции находится внутри блока кода, оно видно повсюду внутри этого блока. Но не за его пределами.
Например, давайте представим, что нам нужно объявить функцию welcome() в зависимости от переменной age , которую мы получаем во время выполнения. И потом мы планируем использовать его некоторое время спустя.
Если мы используем объявление функции, оно не будет работать должным образом:
let age = Prompt("Сколько вам лет?", 18);
// условно объявляем функцию
если (возраст <18) {
функция добро пожаловать() {
Предупреждение("Привет!");
}
} еще {
функция добро пожаловать() {
Оповещение("Привет!");
}
}
// ...используем позже
добро пожаловать(); // Ошибка: приветствие не определеноЭто связано с тем, что объявление функции видно только внутри блока кода, в котором оно находится.
Вот еще один пример:
пусть возраст = 16; // возьмем 16 в качестве примера
если (возраст <18) {
добро пожаловать(); // (запускается)
// |
функция приветствия() { // |
Предупреждение("Привет!"); // | Объявление функции доступно
} // | везде в блоке, где это объявлено
// |
добро пожаловать(); // / (бежит)
} еще {
функция добро пожаловать() {
Оповещение("Привет!");
}
}
// Здесь у нас закончились фигурные скобки,
// поэтому мы не можем видеть объявления функций, сделанные внутри них.
добро пожаловать(); // Ошибка: приветствие не определено Что мы можем сделать, чтобы welcome было видимым за пределами if ?
Правильным подходом было бы использовать функциональное выражение и назначить welcome переменной, которая объявлена вне if и имеет надлежащую видимость.
Этот код работает по назначению:
let age = Prompt("Сколько вам лет?", 18);
пусть приветствуют;
если (возраст <18) {
добро пожаловать = функция() {
Предупреждение("Привет!");
};
} еще {
добро пожаловать = функция() {
Оповещение("Привет!");
};
}
добро пожаловать(); // ок, сейчас Или мы могли бы упростить его еще больше, используя оператор вопросительного знака ? :
let age = Prompt("Сколько вам лет?", 18);
пусть добро пожаловать = (возраст <18 лет) ?
function() { alert("Привет!"); } :
function() { alert("Привет!"); };
добро пожаловать(); // ок, сейчасКогда выбирать объявление функции, а не выражение функции?
Как правило, когда нам нужно объявить функцию, первое, что нужно учитывать, — это синтаксис объявления функции. Это дает больше свободы в организации нашего кода, поскольку мы можем вызывать такие функции до их объявления.
Это также лучше для удобочитаемости, поскольку легче искать в коде function f(…) {…} , чем let f = function(…) {…}; . Объявления функций более «привлекательны».
…Но если объявление функции нас по какой-то причине не устраивает или нам нужно условное объявление (мы только что видели пример), то следует использовать выражение функции.
Функции — это значения. Их можно назначить, скопировать или объявить в любом месте кода.
Если функция объявлена как отдельный оператор в основном потоке кода, это называется «Объявлением функции».
Если функция создается как часть выражения, она называется «Функциональное выражение».
Объявления функций обрабатываются до выполнения блока кода. Их видно повсюду в квартале.
Выражения функций создаются, когда поток выполнения достигает их.
В большинстве случаев, когда нам нужно объявить функцию, объявление функции предпочтительнее, поскольку оно видно до самого объявления. Это дает нам больше гибкости в организации кода и, как правило, более читабельно.
Поэтому нам следует использовать выражение функции только в том случае, если объявление функции не подходит для этой задачи. Мы видели несколько таких примеров в этой главе и увидим больше в будущем.