# Функции Зачастую нам надо повторять одно и то же действие во многих частях программы. Например, красиво вывести сообщение необходимо при приветствии посетителя, при выходе посетителя с сайта, еще где-нибудь. Чтобы не повторять один и тот же код во многих местах, придуманы функции. Функции являются основными "строительными блоками" программы. [cut] Примеры встроенных функций вы уже видели -- это `alert(message)`, `prompt(message, default)` и `confirm(question)`. Но можно создавать и свои. ## Объявление Пример объявления функции: ```js function showMessage() { alert('Привет всем присутствующим!'); } ``` Вначале идет ключевое слово `function`, после него *имя функции*, затем *список параметров* в скобках (в примере выше он пустой) и *тело функции* -- код, который выполняется при её вызове. Объявленная функция доступна по имени, например: ```js //+ run function showMessage() { alert('Привет всем присутствующим!'); } *!* showMessage(); showMessage(); */!* ``` Этот код выведет сообщение два раза. Уже здесь видна **главная цель создания функций: избавление от дублирования кода**. Если понадобится поменять сообщение или способ его вывода -- достаточно изменить его в одном месте: в функции, которая его выводит. ## Локальные переменные Функция может содержать *локальные* переменные, объявленные через `var`. Такие переменные видны только внутри функции: ```js //+ run function showMessage() { *!* var message = 'Привет, я - Вася!'; // локальная переменная */!* alert(message); } showMessage(); // 'Привет, я - Вася!' alert(message); // <-- будет ошибка, т.к. переменная видна только внутри ``` **Блоки `if/else`, `switch`, `for`, `while`, `do..while` не влияют на область видимости переменных.** При объявлении переменной в таких блоках, она всё равно будет видна во всей функции. Например: ```js function count() { for (*!*var*/!* i=0; i<3; i++) { *!*var*/!* j = i * 2; } *!* alert(i); // i=3, на этом значении цикл остановился alert(j); // j=4, последнее значение, на котором цикл сработал, было i=2 */!* } ``` **Неважно, где именно в функции и сколько раз объявляется переменная. Любое объявление срабатывает один раз и распространяется на всю функцию.** Объявления переменных в примере выше можно передвинуть вверх, это ни на что не повлияет: ```js function count() { *!* var i, j; // передвинули объявления var в начало */!* for (i=0; i<3; i++) { j = i * 2; } alert(i); // i=3 alert(j); // j=4 } ``` ## Внешние переменные Функция может обратиться ко внешней переменной, например: ```js //+ run var *!*userName*/!* = 'Вася'; function showMessage() { var message = 'Привет, я ' + *!*userName*/!*; alert(message); } showMessage(); // Привет, я Вася ``` Доступ возможен не только на чтение, но и на запись. При этом, так как переменная внешняя, то изменения будут видны и снаружи функции: ```js //+ run var *!*userName*/!* = 'Вася'; function showMessage() { *!* userName = 'Петя'; // (1) присвоение во внешнюю переменную */!* var message = 'Привет, я ' + *!*userName*/!*; alert(message); } showMessage(); *!* alert(userName); // Петя, значение внешней переменной изменено функцией */!* ``` Конечно, если бы внутри функции, в строке `(1)`, была бы объявлена своя локальная переменная `var userName`, то все обращения использовали бы её, и внешняя переменная осталась бы неизменной. [summary] **Переменные, объявленные на уровне всего скрипта, называют *"глобальными переменными"*.** Делайте глобальными только те переменные, которые действительно имеют общее значение для вашего проекта. Пусть каждая функция работает "в своей песочнице". [/summary] [warn header="Внимание: неявное объявление глобальных переменных!"] В старом стандарте JavaScript существовала возможность неявного объявления переменных присвоением значения. Например: ```js //+ run function showMessage() { message = 'Привет'; // без var! } showMessage(); alert(message); // Привет ``` В коде выше переменная `message` нигде не объявлена, а сразу присваивается. Скорее всего, программист просто забыл поставить `var`. При `use strict` такой код привёл бы к ошибке, но без него переменная будет создана автоматически, причём в примере выше она создаётся не в функции, а на уровне всего скрипта. Избегайте этого. Здесь опасность даже не в автоматическом создании переменной, а в том, что глобальные переменные должны использоваться тогда, когда действительно нужны "общескриптовые" параметры. Забыли `var` в одном месте, потом в другом -- в результате одна функция неожиданно поменяла глобальную переменную, которую использует другая. Возможна ошибка и потеря времени на поиск проблемы. [/warn] В будущем, когда мы лучше познакомимся с основами JavaScript, в главе [](/closures), мы более детально рассмотрим внутренние механизмы работы переменных и функций. ## Параметры При вызове функции ей можно передать данные, которые та использует по своему усмотрению. Например, этот код выводит два сообщения: ```js //+ run function showMessage(*!*from, text*/!*) { // параметры from, text from = "** " + from + " **"; // здесь может быть сложный код оформления alert(from + ': ' + text); } *!* showMessage('Маша', 'Привет!'); showMessage('Маша', 'Как дела?'); */!* ``` **Параметры копируются в локальные переменные функции**. Например, в коде ниже есть внешняя переменная `from`, значение которой при запуске функции копируется в параметр функции с тем же именем. Далее функция работает уже с параметром: ```js //+ run function showMessage(from, text) { *!* from = '**' + from + '**'; // меняем локальную переменную (1) */!* alert(from + ': ' + text); } var from = "Маша"; showMessage(from, "Привет"); alert(from); // "Маша", без изменений, так как в строке (1) была изменена копия значения ``` Здесь есть небольшая тонкость при работе с объектами. Как мы помним, в переменной хранится ссылка на объект. Поэтому функция, получив параметр-объект, работает с самим этим объектом: Например, в коде ниже функция по ссылке меняет содержимое объекта `user`: ```js //+ run function makeAdmin(user) { user.isAdmin = true; } var user = { name: "Вася" }; makeAdmin(user); alert(user.isAdmin); // true ``` ## Стиль объявления функций В объявлении функции есть правила для расстановки пробелов. Они отмечены стрелочками: Конечно, вы можете ставить пробелы и по-другому, но эти правила используются в большинстве JavaScript-фреймворков. ## Аргументы по умолчанию Функцию можно вызвать с любым количеством аргументов. Если параметр не передан при вызове -- он считается равным `undefined`. Например, функцию показа сообщения `showMessage(from, text)` можно вызвать с одним аргументом: ```js showMessage("Маша"); ``` При этом можно проверить, и если параметр не передан -- присвоить ему значение "по умолчанию": ```js //+ run function showMessage(from, text) { *!* if (text === undefined) { text = 'текст не передан'; } */!* alert(from + ": " + text); } showMessage("Маша", "Привет!"); // Маша: Привет! *!* showMessage("Маша"); // Маша: текст не передан */!* ``` **При объявлении функции необязательные аргументы, как правило, располагают в конце списка.** Для указания значения "по умолчанию", то есть, такого, которое используется, если аргумент не указан, используется два способа:
  1. Можно проверить, равен ли аргумент `undefined`, и если да -- то записать в него значение по умолчанию. Этот способ продемонстрирован в примере выше.
  2. Использовать оператор `||`: ```js //+ run function showMessage(from, text) { text = text || 'текст не передан'; ... } ``` Второй способ считает, что аргумент отсутствует, если передана пустая строка, `0`, или вообще любое значение, которое в булевом виде является `false`.
Если аргументов передано больше, чем надо, например `showMessage("Маша", "привет", 1, 2, 3)`, то ошибки не будет. Но, чтобы получить такие "лишние" аргументы, нужно будет прочитать их из специального объекта `arguments`, который мы рассмотрим в главе [](/arguments-pseudoarray). ## Возврат значения Функция может возвратить результат, который будет передан в вызвавший её код. Например, создадим функцию `calcD`, которая будет возвращать дискриминант квадратного уравнения по формуле b2 - 4ac: ```js //+ run function calcD(a, b, c) { *!*return*/!* b*b - 4*a*c; } var test = calcD(-4, 2, 1); alert(test); // 20 ``` **Для возврата значения используется директива `return`.** Она может находиться в любом месте функции. Как только до нее доходит управление -- функция завершается и значение передается обратно. Вызовов `return` может быть и несколько, например: ```js //+ run function checkAge(age) { if (age > 18) { return true; } else { return confirm('Родители разрешили?'); } } var age = prompt('Ваш возраст?'); if (checkAge(age)) { alert('Доступ разрешен'); } else { alert('В доступе отказано'); } ``` Директива `return` может также использоваться без значения, чтобы прекратить выполнение и выйти из функции. Например: ```js function showMovie(age) { if (!checkAge(age)) { *!* return; */!* } alert("Фильм не для всех"); // (*) // ... } ``` В коде выше, если сработал `if`, то строка `(*)` и весь код под ней никогда не выполнится, так как `return` завершает выполнение функции. [smart header="Значение функции без `return` и с пустым `return`"] В случае, когда функция не вернула значение или `return` был без аргументов, считается что она вернула `undefined`: ```js //+ run function doNothing() { /* пусто */ } alert( doNothing() ); // undefined ``` Обратите внимание, никакой ошибки нет. Просто возвращается `undefined`. Ещё пример, на этот раз с `return` без аргумента: ```js //+ run function doNothing() { return; } alert( doNothing() === undefined ); // true ``` [/smart] ## Выбор имени функции [#function-naming] Имя функции следует тем же правилам, что и имя переменной. Основное отличие -- оно должно быть глаголом, т.к. функция -- это действие. Как правило, используются глагольные префиксы, обозначающие общий характер действия, после которых следует уточнение. Функции, которые начинаются с `"show"` -- что-то показывают: ```js showMessage(..) // префикс show, "показать" сообщение ``` Функции, начинающиеся с `"get"` -- получают, и т.п.: ```js getAge(..) // get, "получает" возраст calcD(..) // calc, "вычисляет" дискриминант createForm(..) // create, "создает" форму checkPermission(..) // check, "проверяет" разрешение, возвращает true/false ``` Это очень удобно, поскольку взглянув на функцию -- мы уже примерно представляем, что она делает, даже если функцию написал совсем другой человек, а в отдельных случаях -- и какого вида значение она возвращает. [smart header="Одна функция -- одно действие"] Функция должна делать только то, что явно подразумевается её названием. И это должно быть одно действие. Если оно сложное и подразумевает поддействия -- может быть имеет смысл выделить их в отдельные функции? Зачастую это имеет смысл, чтобы лучше структурировать код. **...Но самое главное -- в функции не должно быть ничего, кроме самого действия и поддействий, неразрывно связанных с ним.** Например, функция проверки данных (скажем, `"validate"`) не должна показывать сообщение об ошибке. Её действие -- проверить. [/smart] [smart header="Сверхкороткие имена функций"] Имена функций, которые используются *очень часто*, иногда делают сверхкороткими. Например, во фреймворке [jQuery](http://jquery.com) есть функция `$`, во фреймворке [Prototype](http://prototypejs.com) -- функция `$$`, а в библиотеке [LoDash](http://lodash.com/) очень активно используется функция с названием из одного символа подчеркивания `_`. [/smart] ## Итого Объявление функции имеет вид: ```js function имя(параметры, через, запятую) { код функции } ``` При обращении к необъявленной переменной функция будет искать внешнюю переменную с таким именем, но лучше, если функция использует только локальные переменные: Именование функций: Функции являются основными строительными блоками скриптов. Мы будем неоднократно возвращаться к ним и изучать все более и более глубоко.