# Functions Quite often we need to perform a similar action in many places of the script. For example, we need to show a nice-looking message when a visitor logs in, logs out and maybe somewhere else. Functions are the main "building blocks" of the program. They allow the code to be called many times without repetition. [cut] We've already seen examples of built-in functions, like `alert(message)`, `prompt(message, default)` and `confirm(question)`. But we can create functions of our own as well. ## Definition An example of a function definition: ```js function showMessage() { alert( 'Hello everyone!' ); } ``` The `function` keyword goes first, then follows the *name of the function*, then a list of *parameters* in the brackets (empty in the example above) and finally the code of the function, also named "the function body". Once defined, the function can be called by it's name. For instance: ```js //+ run function showMessage() { alert( 'Hello everyone!' ); } *!* showMessage(); showMessage(); */!* ``` The call executes the code of the function. Here we will see the message shown two times. In this example we can see one of the main purposes of the functions: to evade code duplication. If we ever need to change the message or the way it is shown -- it's enough to modify the code in one place: the function which outputs it. ## Local variables A function may declare *local* variables with `var`, `let` or `const`. These variables are only seen from inside the function: ```js //+ run function showMessage() { *!* var message = "Hello, I'm JavaScript!"; // local variable */!* alert( message ); } showMessage(); // Hello, I'm JavaScript! alert( message ); // <-- Error! The variable is local to the function ``` **Блоки `if/else`, `switch`, `for`, `while`, `do..while` не влияют на область видимости переменных.** При объявлении переменной в таких блоках, она всё равно будет видна во всей функции. Например: ```js //+ no-beautify function count() { // переменные i,j не будут уничтожены по окончании цикла for (*!*var*/!* i = 0; i < 3; i++) { *!*var*/!* j = i * 2; } *!* alert( i ); // i=3, последнее значение i, при нём цикл перестал работать alert( j ); // j=4, последнее значение j, которое вычислил цикл */!* } ``` **Неважно, где именно в функции и сколько раз объявляется переменная. Любое объявление срабатывает один раз и распространяется на всю функцию.** Объявления переменных в примере выше можно передвинуть вверх, это ни на что не повлияет: ```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 no-beautify 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`, то все обращения использовали бы её, и внешняя переменная осталась бы неизменной. **Переменные, объявленные на уровне всего скрипта, называют *"глобальными переменными"*.** В примере выше переменная `userName` -- глобальная. Делайте глобальными только те переменные, которые действительно имеют общее значение для вашего проекта, а нужные для решения конкретной задачи -- пусть будут локальными в соответствующей функции. [warn header="Внимание: неявное объявление глобальных переменных!"] В старом стандарте JavaScript существовала возможность неявного объявления переменных присвоением значения. Например: ```js //+ run function showMessage() { message = 'Привет'; // без var! } showMessage(); alert( message ); // Привет ``` В коде выше переменная `message` нигде не объявлена, а сразу присваивается. Скорее всего, программист просто забыл поставить `var`. При `use strict` такой код привёл бы к ошибке, но без него переменная будет создана автоматически, причём в примере выше она создаётся не в функции, а на уровне всего скрипта. Избегайте этого. Здесь опасность даже не в автоматическом создании переменной, а в том, что глобальные переменные должны использоваться тогда, когда действительно нужны "общескриптовые" параметры. Забыли `var` в одном месте, потом в другом -- в результате одна функция неожиданно поменяла глобальную переменную, которую использует другая. И поди разберись, кто и когда её поменял, не самая приятная ошибка для отладки. [/warn] В будущем, когда мы лучше познакомимся с основами JavaScript, в главе [](/closures), мы более детально рассмотрим внутренние механизмы работы переменных и функций. ## Параметры При вызове функции ей можно передать данные, которые та использует по своему усмотрению. Например, этот код выводит два сообщения: ```js //+ run no-beautify function showMessage(*!*from, text*/!*) { // параметры from, text from = "** " + from + " **"; // здесь может быть сложный код оформления alert(from + ': ' + text); } *!* showMessage('Маша', 'Привет!'); showMessage('Маша', 'Как дела?'); */!* ``` **Параметры копируются в локальные переменные функции**. Например, в коде ниже есть внешняя переменная `from`, значение которой при запуске функции копируется в параметр функции с тем же именем. Далее функция работает уже с параметром: ```js //+ run function showMessage(from, text) { *!* from = '**' + from + '**'; // меняем локальную переменную from */!* alert( from + ': ' + text ); } var from = "Маша"; showMessage(from, "Привет"); alert( from ); // старое значение from без изменений, в функции была изменена копия ``` ## Аргументы по умолчанию Функцию можно вызвать с любым количеством аргументов. Если параметр не передан при вызове -- он считается равным `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 no-beautify 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 //+ no-beautify showMessage(..) // префикс show, "показать" сообщение ``` Функции, начинающиеся с `"get"` -- получают, и т.п.: ```js //+ no-beautify 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 имя(параметры, через, запятую) { код функции } ``` При обращении к необъявленной переменной функция будет искать внешнюю переменную с таким именем, но лучше, если функция использует только локальные переменные: Именование функций: Функции являются основными строительными блоками скриптов. Мы будем неоднократно возвращаться к ним и изучать все более и более глубоко.