136 lines
5.9 KiB
Markdown
136 lines
5.9 KiB
Markdown
# Именованные функциональные выражения
|
||
|
||
Обычно то, что называют "именем функции", на самом деле -- всего лишь имя переменной, в которую присвоена функция. К самой функции это "имя" никак не привязано.
|
||
|
||
Однако, есть в JavaScript способ указать имя, действительно привязанное к функции. Оно называется "Named Function Expression" (сокращённо NFE) или, по-русски, *"именованное функциональное выражение"*.
|
||
[cut]
|
||
|
||
|
||
## Named Function Expression [#functions-nfe]
|
||
|
||
Простейший пример NFE выглядит так:
|
||
|
||
```js
|
||
var f = function *!*sayHi*/!*(...) { /* тело функции */ };
|
||
```
|
||
|
||
Проще говоря, NFE -- это `Function Expression` с дополнительным именем (в примере выше `sayHi`).
|
||
|
||
Что же это за имя, которое идёт в дополнение к переменной `f`, и зачем оно?
|
||
|
||
**Имя функционального выражения (`sayHi`) имеет особый смысл. Оно доступно только изнутри самой функции.**
|
||
|
||
**Это ограничение видимости входит в стандарт JavaScript и поддерживается всеми браузерами, кроме IE8-.**
|
||
|
||
Например:
|
||
|
||
```js
|
||
//+ run
|
||
var f = function sayHi(name) {
|
||
alert(sayHi); // изнутри функции - видно (выведет код функции)
|
||
};
|
||
|
||
alert(sayHi); // снаружи - не видно (ошибка: undefined variable 'sayHi')
|
||
```
|
||
|
||
**Кроме того, имя NFE нельзя перезаписать:**
|
||
|
||
```js
|
||
//+ run
|
||
var test = function sayHi(name) {
|
||
*!*
|
||
sayHi = "тест"; // перезапись
|
||
*/!*
|
||
alert(sayHi); // function... (перезапись не удалась)
|
||
};
|
||
|
||
test();
|
||
```
|
||
|
||
В режиме `use strict` код выше выдал бы ошибку.
|
||
|
||
**Как правило, имя NFE используется для единственной цели -- позволить изнутри функции вызвать саму себя.**
|
||
|
||
[smart header="Устаревшее специальное значение `arguments.callee`"]
|
||
Если вы работали с JavaScript, то, возможно, знаете, что для этой цели также служило специальное значение `arguments.callee`.
|
||
|
||
Если это название вам ни о чём не говорит -- всё в порядке, читайте дальше, мы обязательно обсудим его [в отдельной главе](#arguments-callee).
|
||
|
||
Если же вы в курсе, то стоит иметь в виду, что оно официально исключено из современного стандарта. А NFE -- это наше настоящее.
|
||
[/smart]
|
||
|
||
## Пример использования
|
||
|
||
NFE используется в первую очередь в тех ситуациях, когда функцию нужно передавать в другое место кода или перемещать из одной переменной в другую.
|
||
|
||
**Внутреннее имя позволяет функции надёжно обращаться к самой себе, где бы она ни находилась.**
|
||
|
||
Вспомним, к примеру, функцию-факториал из задачи [](/task/factorial):
|
||
|
||
```js
|
||
//+ run
|
||
function f(n) {
|
||
return n ? n*f(n-1) : 1;
|
||
};
|
||
|
||
alert( f(5) ); // 120
|
||
```
|
||
|
||
Попробуем перенести её в другую переменную `g`:
|
||
|
||
```js
|
||
//+ run
|
||
function f(n) {
|
||
return n ? n*f(n-1) : 1;
|
||
};
|
||
|
||
*!*
|
||
var g = f;
|
||
f = null;
|
||
*/!*
|
||
|
||
alert( g(5) ); // запуск функции с новым именем - ошибка при выполнении!
|
||
```
|
||
|
||
Ошибка возникла потому что функция из своего кода обращается к своему старому имени `f`. А этой функции уже нет, `f = null`.
|
||
|
||
Для того, чтобы функция всегда надёжно работала, объявим её как Named Function Expression:
|
||
|
||
```js
|
||
//+ run
|
||
var f = function *!*factorial*/!*(n) {
|
||
return n ? n**!*factorial*/!*(n-1) : 1;
|
||
};
|
||
|
||
var g = f; // скопировали ссылку на функцию-факториал в g
|
||
f = null;
|
||
|
||
*!*
|
||
alert( g(5) ); // 120, работает!
|
||
*/!*
|
||
```
|
||
|
||
[warn header="В браузере IE8- создаются две функции"]
|
||
|
||
Как мы говорили выше, в браузере IE до 9 версии имя NFE видно везде, что является ошибкой с точки зрения стандарта.
|
||
|
||
...Но на самом деле ситуация еще забавнее. Старый IE создаёт в таких случаях целых две функции: одна записывается в переменную `f`, а вторая -- в переменную `factorial`.
|
||
|
||
Например:
|
||
|
||
```js
|
||
//+ run
|
||
var f = function factorial(n) { /*...*/ };
|
||
|
||
// в IE8- false
|
||
// в остальных браузерах ошибка, т.к. имя factorial не видно
|
||
alert(f === factorial);
|
||
```
|
||
|
||
Все остальные браузеры полностью поддерживают именованные функциональные выражения.
|
||
[/warn]
|
||
|
||
## Итого
|
||
|
||
Если функция задана как Function Expression, её можно дать имя. Оно будет доступно только внутри функции (кроме IE8-) и предназначено для надёжного рекурсивного вызова функции, даже если она записана в другую переменную.
|
||
|