Синтаксис,
который мы использовали на предыдущем занятии для объявления функций,
называется Function Declaration (объявление функции):
function showMsg() {
console.log("Hello!");
}
Существует ещё
один синтаксис создания функций, который называется Function Expression (функциональное
выражение). Оно выглядит вот так:
let showMsg = function() {
console.log("Hello!");
};
Здесь мы создаем
функцию и явно присваиваем ее переменной showMsg. На самом деле
в предыдущем примере происходит то же самое, только записано в неявном виде. Обратите
внимание, в конце следует ставить точку с запятой, т.к. мы инициализируем
переменную showMsg по синтаксису:
let <переменная> = <значение>;
Значение этой
переменной можно вывести в консоль и посмотреть чему она равна:
console.log( showMsg );
console.log( typeof showMsg );
Да, переменная showMsg содержит искомую
функцию, точнее ссылку на функцию. А вот тип переменной – это специальный тип
данных, означающий функцию. На самом деле – это объект JavaScript специального
вида. Если мы вернем запись функции по синтаксису Function Declaration, то при
выводе в консоль ничего не изменится. Это как раз и говорит, что оба объявления
подобны. Подобны, но не идентичны! Об их различиях речь пойдет позже в этом
занятии.
Вернем запись в
виде Function Expression. Здесь у нас
имеется переменная showMsg, которая ссылается на функцию. А раз
так, то можем ли мы ее скопировать в другую переменную и получить копию этой
функции? Почти, только мы скопируем не саму функцию, а ссылку на нее, в
результате обе переменные будут обращаться к одной и той же функции:
Теперь вызовем функцию
через эти переменные. Для этого после их имени нужно поставить круглые скобки:
show(); // 1-й вызов функции
showMsg(); //2-й вызов функции
Запомните этот
момент! Без круглых скобок мы работаем с переменными show и showMsg, а с круглыми
скобками – вызываем соответствующие функции. Этот синтаксис следует правильно
понимать и помнить.
Далее, все это
будет работать, если функцию объявить и как Function Declaration, так как обе
записи – это, по сути, одно и тоже: создается переменная showMsg, которая ссылается
на объект-функцию.
Ну, хорошо,
разобрались. Но зачем нужно объявление по Function Expression? Для ответа на
этот вопрос приведем такой пример. Предположим, у нас есть функция, которая
запрашивает у пользователя согласие на использование cookies в браузере. И реализуем ее так:
function agreeCookies(question, yes, no) {
if (confirm(question)) yes();
else no();
}
function agreeYes() {
console.log("Вы приняли соглашение о cookies");
}
function agreeNo() {
console.log("Вы отказались от использования cookies");
}
agreeCookies("Наш сайт использует cookies. Нам нужно ваше согласие", agreeYes, agreeNo);
Смотрите, у
функции agreeCookies три параметра:
первый отвечает за текст сообщения, а два других – содержат ссылки на функции,
которые вызываются при согласии или несогласии с использованием cookies. В результате
мы получили довольно гибкий код: вся логика по обработке вынесена в отдельные
функции, которые могут обрабатывать запрос самыми разными способами. При этом
сама функция agreeCookies остается
неизменной. Аргументы yes, no, которые
содержат ссылки на функции, еще называют callback-функциями (от
англ. call back – обратный
вызов), то есть, отложенный вызов, если в нем будет необходимость.
Данный пример
можно записать и короче, вот так:
function agreeCookies(question, yes, no) {
if (confirm(question)) yes();
else no();
}
agreeCookies(
"Наш сайт использует cookies. Нам нужно ваше согласие",
function () { console.log("Вы приняли соглашение о cookies"); },
function () { console.log("Вы отказались от использования cookies"); }
);
Мы здесь
непосредственно при вызове функции agreeCookies передаем ей все
необходимые параметры: строку запроса и две функции, которые объявлены прямо
внутри вызова agreeCookies. У них нет имен,
поэтому такие функции называются анонимными. Соответственно, эти функции
доступны и существуют только в момент вызова agreeCookies и не существуют
за ее пределами.
Программисты на JavaScript очень часто
используют такой подход при написании скриптов. И это считается хорошим стилем
в программировании.
Итак, можно
заключить, что имя функции – это переменная, которая содержит специальные
данные – функцию. Эти данные можно воспринимать не как обычные статичные
значения, а как некое действие, которое может быть выполнено в любом месте
скрипта.
Теперь посмотрим
на отличия между двумя объявлениями функций по Function Declaration и Function Expression. Как мы
говорили на предыдущем занятии, функции объявленные по Function Declaration:
function agreeCookies(question) {
if (confirm(question)) console.log("Вы приняли соглашение о cookies");
else console.log("Вы отказались от использования cookies");
}
Могут вызываться
и до и после их объявления, то есть вот так:
agreeCookies("Наш сайт использует cookies. Нам нужно ваше согласие");
function agreeCookies(question) {
if (confirm(question)) console.log("Вы приняли соглашение о cookies");
else console.log("Вы отказались от использования cookies");
}
agreeCookies("Наш сайт использует cookies. Нам нужно ваше согласие");
Если же мы
объявим эту же функцию как Function Expression:
let agreeCookies = function(question) {
if (confirm(question)) console.log("Вы приняли соглашение о cookies");
else console.log("Вы отказались от использования cookies");
};
То вызвать ее до
объявления переменной agreeCookies будет
невозможно, так как она не будет существовать. А вот после объявления вызовется
без проблем.
Далее, если у
нас включен строгий режим "use strict" (а мы договорились его
использовать), то объявления по Function Declaration видны только внутри блока
кода, в котором они объявлены. Например, вот в такой программе:
let age = prompt("Сколько вам лет?", 18);
if(age < 18) {
function setAccess() {
console.log("Доступ запрещен: вы слишком молоды");
}
}
else {
function setAccess() {
console.log("Доступ разрешен");
}
}
setAccess();
В блоках if else объявляется
функция setAccess, которую
предполагается вызвать и определить: разрешать доступ или нет. Но здесь
возникнет ошибка, так как функция не будет существовать за пределами блоков if else. Это
ограничение можно обойти, используя Function Expression:
let age = prompt("Сколько вам лет?", 18);
let setAccess = null;
if(age < 18) {
setAccess = function () {
console.log("Доступ запрещен: вы слишком молоды");
};
}
else {
setAccess = function () {
console.log("Доступ разрешен");
};
}
setAccess();
Теперь
переменная setAccess будет содержать
ссылку на функцию, в зависимости от указанного возраста и после блока if else вызывается без
проблем. Кстати, эту же программу можно записать гораздо короче, вот так:
let age = prompt("Сколько вам лет?", 18);
let setAccess = (age < 18) ?
function () {console.log("Доступ запрещен: вы слишком молоды"); } :
function () { console.log("Доступ разрешен"); };
setAccess();
Здесь переменной
setAccess будет присваивается
анонимная функция в зависимости от указанного возраста.
Так как же
понимать: когда использовать объявления по Function Declaration, а когда по Function Expression? Обычно,
поступают так. В первую очередь следует рассматривать синтаксис Function
Declaration. Он дает больше свободы в организации кода: функции, объявленные
таким образом, можно вызывать и до и после их объявления. К тому же такие
функции проще воспринимаются в тексте скрипта.
Объявления по Function Expression следует
использовать только тогда, когда по каким-то причинам нам не подходит объявление
по Function Declaration. Например, при реализации callback-функций. Или
вызова функции за пределами блока их объявления. Все эти примеры мы с вами
только что рассматривали.