Обычно у функций в JavaScript нет имени. Как мы говорили ранее, функция является всего лишь значением, которое присваивается переменной.
Имя переменной для краткости называют «именем функции», но на самом деле идёт подмена понятий. Ведь это имя к самой функции никак не привязано. Если функцию переместить в другую переменную — она сменит «имя»:
function f() { alert(1); }
g = f;
f = 0;
g(); // сменили имя f на g!
Однако, есть в JavaScript возможность указать имя, действительно привязанное к функции. Она называется Named Function Expression (NFE) или, по-русски, именованное функциональное выражение.
Named Function Expression (именованное функциональное выражение) выглядит так:
var f = function *!*sayHi*/!*(...) { /* тело функции */ };
Проще говоря, это Function Expression (функция в составе более сложного выражения var f = ...), но дополнительно с именем sayHi.
Что же это за имя, которое идёт в дополнение к переменной f, и зачем оно?
Имя функционального выражения (sayHi) имеет особый смысл. Оно доступно только изнутри самой функции.
Это ограничение видимости входит в стандарт JavaScript и поддерживается всеми браузерами, кроме IE<9.
Например:
var f = function sayHi(name) {
alert(sayHi); // изнутри функции - видно (выведет код функции)
}
alert(sayHi); // снаружи - не видно (ошибка: undefined variable 'sayHi')
Такое имя жёстко привязано к функции и служит единственной цели — позволить функции вызвать саму себя.
В старых версиях JavaScript для этого служило специальное свойство arguments.callee. Сейчас оно официально устарело, но мы обязательно обсудим его в отдельной главе.
При этом его ценность заключается в том, что оно работает всегда, даже если переменная, в которой изначально содержалась функция, была перезаписана, а сама функция — перемещена в другое место.
Например, создадим функцию-факториал из задачи Вычислить факториал как Named Function Expression:
var f = function factorial(n) {
return n ? n*factorial(n-1) : 1;
}
Почему мы решили ее создать именно так, а не как Function Declaration? Причин может быть множество, например условное создание функции внутри if, при котором Function Declaration не работает.
После создания функции, даже если переменная f перезаписана, функция сможет обращаться к самой себе через имя factorial:
var f = function *!*factorial*/!*(n) {
return n ? n**!*factorial*/!*(n-1) : 1;
}
var g = f; // скопировали ссылку на функцию-факториал в g
f = function() { return 0; }; // перезаписали f
*!*
alert( g(5) ); // 120, работает!
*/!*
Сравним это с обычным Function Expression:
var f = function(n) {
return n ? n**!*f(n-1)*/!* : 1;
}
var g = f;
f = function() { return 0; };
*!*
alert( g(5) ); // 0, т.к. функция попытается вызвать f(n-1), а f перезаписана
*/!*
В браузере IE до 9 версии такое имя видно везде, что является ошибкой с точки зрения стандарта.
На самом деле ситуация еще забавнее.. Старый IE создает в таких случаях целых две фунции, то есть считает это двойным объявлением.
var f = function factorial(n) { /*...*/ }
// в IE<9 false
// в остальных браузерах ошибка, т.к. имя factorial видно только внутри
alert(f === factorial);
Все остальные браузеры поддерживают именованные функциональные выражения.
Каков будет результат выполнения кода? Почему?
(function g() { return 1; });
alert(g);
Ответ: ошибка везде кроме IE<9:
Начнем со скобок. Если бы их вокруг function не было, то результат был бы очевиден: код функции.
// обычное объявление функции (Function Declaration)
function g() { return 1; };
alert(g);
..Но скобки есть, и это даёт ошибку при alert(g):
(function g() { return 1; }); // Named Function Expression!
alert(g);
Ключ к решению — понимание, что функция в скобках (function ... ) являтся Function Expression, а значит у нас есть Named Function Expression, имя которого g видно только внутри функции.
Все браузеры, кроме IE<9 поддерживают это ограничение видимости и выведут ошибку ‘undefined variable’.
Что будет при повторном вызове функции?
var f = function *!*sayHi*/!*(n) {
alert(n);
*!*sayHi = 0;*/!* // перезаписываем имя функции
}
f(1); // что выведет?
f(2); // а теперь что выведет?
Разумеется, в жизни мы не делаем такого, но разобрать ситуацию важно для общего понимания функций.
Ничего особенного не произойдет, функция будет работать как обычно:
var f = function *!*sayHi*/!*(n) {
alert(n);
*!*sayHi = 0;*/!*
}
f(1); // 1
f(2); // 2
Дело в том, что переменная f хранит ссылку на функцию.
Изнутри функции создается переменная sayHi, которая тоже ссылается на функцию. Если мы перезапишем её, то просто будет на одну ссылку на функцию меньше. Ну и что…
Комментарии
- Приветствуются комментарии, содержащие дополнения и вопросы по статье, и ответы на них.
- Если ваш комментарий касается задачи -- откройте её в отдельном окне и напишите там.
- Комментарии без смысла, с рекламой или не о статье вообще - удаляются.