В 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(…) {…};
. Объявления функций более «привлекательны».
…Но если объявление функции нас по какой-то причине не устраивает или нам нужно условное объявление (мы только что видели пример), то следует использовать выражение функции.
Функции — это значения. Их можно назначить, скопировать или объявить в любом месте кода.
Если функция объявлена как отдельный оператор в основном потоке кода, это называется «Объявлением функции».
Если функция создается как часть выражения, она называется «Функциональное выражение».
Объявления функций обрабатываются до выполнения блока кода. Их видно повсюду в квартале.
Выражения функций создаются, когда поток выполнения достигает их.
В большинстве случаев, когда нам нужно объявить функцию, объявление функции предпочтительнее, поскольку оно видно до самого объявления. Это дает нам больше гибкости в организации кода и, как правило, более читабельно.
Поэтому нам следует использовать выражение функции только в том случае, если объявление функции не подходит для этой задачи. Мы видели несколько таких примеров в этой главе и увидим больше в будущем.