# Функциональные выражения
В JavaScript функция является значением, таким же как строка или число.
## Функция -- это значение
Как и любое значение, объявленную функцию можно вывести, вот так:
```js
//+ run
function sayHi() {
alert( "Привет" );
}
*!*
alert( sayHi ); // выведет код функции
*/!*
```
Обратим внимание на то, что в последней строке после `sayHi` нет скобок. То есть, функция не вызывается, а просто выводится на экран.
**Функцию можно скопировать в другую переменную:**
```js
//+ run
function sayHi() { // (1)
alert( "Привет" );
}
var func = sayHi; // (2)
func(); // Привет // (3)
sayHi = null;
sayHi(); // ошибка (4)
```
- Объявление `(1)` как бы говорит интерпретатору "создай функцию и помести её в переменную `sayHi`
- В строке `(2)` мы копируем функцию в новую переменную `func`. Ещё раз обратите внимание: после `sayHi` нет скобок. Если бы они были, то вызов `var func = sayHi()` записал бы в `func` *результат* работы `sayHi()` (кстати, чему он равен? правильно, `undefined`, ведь внутри `sayHi` нет `return`).
- На момент `(3)` функцию можно вызывать и как `sayHi()` и как `func()`
- ...Однако, в любой момент значение переменной можно поменять. При этом, если оно не функция, то вызов `(4)` выдаст ошибку.
Обычные значения, такие как числа или строки, представляют собой *данные*. А функцию можно воспринимать как *действие*. Это действие, как правило, хранится в переменной, но его можно скопировать или переместить из неё.
## Объявление Function Expression [#function-expression]
Функцию можно создать и присвоить переменной в любом месте кода.
Для этого используется объявление "Function Expression" (функциональное выражение), которое выглядит так:
```js
//+ run
var f = function(параметры) {
// тело функции
};
```
Например:
```js
//+ run
var sayHi = function(person) {
alert("Привет, " + person);
};
sayHi('Вася');
```
## Сравнение с Function Declaration
"Классическое" объявление функции, о котором мы говорили до этого, вида `function имя(параметры) {...}`, называется в спецификации языка "Function Declaration".
- **Function Declaration** -- функция, объявленная в основном потоке кода.
- **Function Expression** -- объявление функции в контексте какого-либо выражения, например присваивания.
Несмотря на немного разный вид, по сути две эти записи делают одно и то же:
```js
// Function Declaration
function sum(a, b) {
return a + b;
}
// Function Expression
var sum = function(a, b) {
return a + b;
}
```
Оба этих объявления говорят интерпретатору: "объяви переменную `sum`, создай функцию с указанными параметрами и кодом и сохрани её в `sum`".
**При этом название переменной, в которую записана функция, обычно называют "именем функции". Говорят: "функция sum". Но при этом имя к функции никак не привязано.**
Это всего лишь имя переменной, в которой *в данный момент* находится функция.
Функция может быть в процессе работы скрипта скопирована в другую переменную, а из этой удалена, передана в другое место кода, и так далее, как мы видели выше.
**Основное отличие между ними: функции, объявленные как Function Declaration, создаются интерпретатором *до выполнения кода*.**
Поэтому их можно вызвать *до* объявления, например:
```js
//+ run
*!*
sayHi("Вася"); // Привет, Вася
*/!*
function sayHi(name) {
alert("Привет, " + name);
}
```
А если бы это было объявление Function Expression, то такой вызов бы не сработал:
```js
//+ run
*!*
sayHi("Вася"); // ошибка!
*/!*
var sayHi = function(name) {
alert("Привет, " + name);
}
```
Это из-за того, что JavaScript перед запуском кода ищет в нём Function Declaration (они начинаются в основном потоке с `function`) и обрабатывает их.
А Function Expression создаются при выполнении выражения, в котором созданы, в данном случае -- функция будет создана при операции присваивания `sayHi = ...`.
**Как правило, возможность Function Declaration вызвать функцию до объявления -- это удобно, так как даёт больше свободы в том, как организовать свой код.**
### Условное объявление функции [#bad-conditional-declaration]
В некоторых случаях "дополнительное удобство" Function Declaration может сослужить плохую службу.
Например, попробуем, в зависимости от условия, объявить функцию `sayHi` по-разному:
```js
//+ run
var age = 20;
if (age >= 18) {
function sayHi() { alert('Прошу вас!'); }
} else {
function sayHi() { alert('До 18 нельзя'); }
}
sayHi();
```
[smart header="Зачем условное объявление?"]
Конечно, можно произвести проверку условия внутри функции.
Но вынос проверки вовне даёт очевидный выигрыш в производительности в том случае, когда проверку достаточно произвести только один раз, и её результат никогда не изменится.
Например, мы можем проверить, поддерживает ли браузер определённые современные возможности, и если да -- функция будет использовать их, а если нет -- реализовать её другим способом. При этом проверка будет осуществляться один раз, на этапе объявления функции, а не при каждом запуске функции.
[/smart]
При запуске примера выше в любом браузере, кроме Firefox, мы увидим, что условное объявление не работает. Срабатывает `"До 18 нельзя"`, несмотря на то, что `age = 20`.
В чём дело? Чтобы ответить на этот вопрос -- вспомним, как работают функции.
- Function Declaration обрабатываются перед запуском кода. Интерпретатор сканирует код и создает из таких объявлений функции. При этом второе объявление перезапишет первое.
- Дальше, во время выполнения, объявления Function Declaration игнорируются (они уже были обработаны). Это как если бы код был таким:
```js
function sayHi() { alert('Прошу вас!'); }
function sayHi() { alert('До 18 нельзя'); }
var age = 20;
if (age >= 18) {
/* объявление было обработано ранее */
} else {
/* объявление было обработано ранее */
}
*!*
sayHi(); // "До 18 нельзя", сработает всегда вторая функция
*/!*
```
...То есть, от `if` здесь уже ничего не зависит. По-разному объявить функцию, в зависимости от условия, не получилось.
Такое поведение соответствует современному стандарту. На момент написания этого раздела ему следуют все браузеры, кроме, как ни странно, Firefox.
**Вывод: для условного объявления функций Function Declaration не годится.**
А что, если использовать Function Expression?
```js
//+ run
var age = prompt('Сколько вам лет?');
var sayHi;
if (age >= 18) {
sayHi = function() { alert('Прошу Вас!'); }
} else {
sayHi = function() { alert('До 18 нельзя'); }
}
sayHi();
```
Или даже так:
```js
//+ run
var age = prompt('Сколько вам лет?');
var sayHi = (age >= 18) ?
function() { alert('Прошу Вас!'); } :
function() { alert('До 18 нельзя'); };
sayHi();
```
Оба этих варианта работают правильно, поскольку, в зависимости от условия, создаётся именно та функция, которая нужна.
### Анонимные функции
Взглянем ещё на один пример.
Функция `test(f, yes, no)` получает три функции, вызывает первую и, в зависимости от её результата, вызывает вторую или третью:
```js
//+ run
*!*
function test(f, yes, no) {
if (f()) yes()
else no();
}
*/!*
// вспомогательные функции
function f1() {
return confirm("OK?");
}
function f2() {
alert("Вы согласились.");
}
function f3() {
alert("Вы отменили выполнение.");
}
// использование
test(f1, f2, f3);
```
В этом примере для нас, наверно, нет ничего нового. Подумаешь, объявили функции `f1`, `f2`, `f3`, передали их в качестве параметров другой функции (ведь функция -- обычное значение), вызвали те, которые нужны...
А вот то же самое, но более коротко:
```js
//+ run
function test(f, yes, no) {
if (f()) yes()
else no();
}
*!*
test(
function() { return confirm("OK?"); },
function() { alert("Вы согласились."); },
function() { alert("Вы отменили выполнение."); }
);
*/!*
```
Здесь функции объявлены прямо внутри вызова `test(...)`, даже без присвоения им имени.
**Функциональное выражение, которое не записывается в переменную, называют [анонимной функцией](http://ru.wikipedia.org/wiki/%D0%90%D0%BD%D0%BE%D0%BD%D0%B8%D0%BC%D0%BD%D0%B0%D1%8F_%D1%84%D1%83%D0%BD%D0%BA%D1%86%D0%B8%D1%8F).**
Действительно, зачем нам записывать функцию в переменную, если мы не собираемся вызывать её ещё раз? Можно просто объявить непосредственно там, где функция нужна.
Такого рода код возникает естественно, он соответствует "духу" JavaScript.
## new Function
Существует ещё один способ создания функции, который используется очень редко.
Он выглядит так:
```js
//+ run
var sum = new Function('a,b', ' return a+b; ');
var result = sum(1,2);
alert(result); // 3
```
То есть, функция создаётся вызовом `new Function(params, code)`:
- `params`
- Параметры функции через запятую в виде строки.
- `code`
- Код функции в виде строки.
Этот способ позволяет конструировать строку с кодом функции динамически, во время выполнения программы. Это, скорее, исключение, чем правило, но также бывает востребовано. Пример использования -- динамическая компиляция шаблонов на JavaScript, мы встретимся с ней позже, при работе с интерфейсами.
## Итого
Функции в JavaScript являются значениями. Их можно присваивать, передавать, создавать в любом месте кода.
- Если функция объявлена в *основном потоке кода*, то это Function Declaration.
- Если функция создана как *часть выражения*, то это Function Expression.
Между этими двумя основными способами создания функций есть следующие различия:
|
Function Declaration |
Function Expression |
| Время создания |
До выполнения первой строчки кода. |
Когда управление достигает строки с функцией. |
| Можно вызвать до объявления |
`Да` (т.к. создаётся заранее) |
`Нет` |
| Условное объявление в `if` |
`Не работает` |
`Работает` |
Иногда в коде начинающих разработчиков можно увидеть много Function Expression. Почему-то, видимо, не очень понимая происходящее, функции решают создавать как `var func = function()`, но в большинстве случаев обычное объявление функции -- лучше.
**Если нет явной причины использовать Function Expression -- предпочитайте Function Declaration.**
Сравните по читаемости:
```js
// Function Expression
var f = function() { ... }
// Function Declaration
function f() { ... }
```
Function Declaration короче и лучше читается. Дополнительный бонус -- такие функции можно вызывать до того, как они объявлены.
Используйте Function Expression только там, где это действительно нужно и удобно.