This commit is contained in:
Ilya Kantor 2014-11-16 01:40:20 +03:00
parent 962caebbb7
commit 87bf53d076
1825 changed files with 94929 additions and 0 deletions

View file

@ -0,0 +1,21 @@
**Первый код выведет `function ...`, второй -- ошибку во всех браузерах, кроме IE8-.**
```js
//+ run untrusted
// обычное объявление функции (Function Declaration)
function g() { return 1; };
alert(g); // функция
```
Во втором коде скобки есть, значит функция внутри является не `Function Declaration`, а частью выражения, то есть `Named Function Expression`. Его имя видно только внутри, снаружи переменная `g` не определена.
```js
//+ run untrusted
// Named Function Expression!
(function g() { return 1; });
alert(g); // Ошибка!
```
Все браузеры, кроме IE8-, поддерживают это ограничение видимости и выведут ошибку, `"undefined variable"`.

View file

@ -0,0 +1,20 @@
# Проверка на NFE
[importance 5]
Каков будет результат выполнения кода?
```js
function g() { return 1; }
alert(g);
```
А такого? Будет ли разница, если да -- почему?
```js
(function g() { return 1; });
alert(g);
```

View file

@ -0,0 +1,136 @@
# Именованные функциональные выражения
Обычно то, что называют "именем функции", на самом деле -- всего лишь имя переменной, в которую присвоена функция. К самой функции это "имя" никак не привязано.
Однако, есть в 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-) и предназначено для надёжного рекурсивного вызова функции, даже если она записана в другую переменную.