Довольно часто нам требуется выполнить подобное действие во многих местах скрипта.
Например, нам нужно показывать красивое сообщение, когда посетитель входит в систему, выходит из системы и, возможно, где-то еще.
Функции являются основными «строительными блоками» программы. Они позволяют вызывать код много раз без повторения.
Мы уже видели примеры встроенных функций, таких как alert(message)
, prompt(message, default)
и confirm(question)
. Но мы также можем создавать собственные функции.
Чтобы создать функцию, мы можем использовать объявление функции .
Это выглядит так:
функция showMessage() { alert('Всем привет!'); }
Сначала идет ключевое слово function
, затем имя функции , затем список параметров в круглых скобках (в приведенном выше примере разделенный запятыми, пустой, примеры мы увидим позже) и, наконец, код функции, также названной «тело функции» между фигурными скобками.
имя функции(параметр1, параметр2, ... параметрN) { // тело }
Нашу новую функцию можно вызывать по ее имени: showMessage()
.
Например:
функция showMessage() { alert('Всем привет!'); } показатьСообщение(); показатьСообщение();
Вызов showMessage()
выполняет код функции. Здесь мы увидим сообщение два раза.
Этот пример наглядно демонстрирует одно из основных предназначений функций: избежать дублирования кода.
Если нам когда-нибудь понадобится изменить сообщение или способ его отображения, достаточно изменить код в одном месте: в функции, которая его выводит.
Переменная, объявленная внутри функции, видна только внутри этой функции.
Например:
функция showMessage() { let message = "Привет, я JavaScript!"; // локальная переменная предупреждение(сообщение); } показатьСообщение(); // Привет, я JavaScript! предупреждение(сообщение); // <-- Ошибка! Переменная является локальной для функции
Функция также может обращаться к внешней переменной, например:
пусть имя пользователя = 'Джон'; функция showMessage() { пусть сообщение = 'Привет' + имя пользователя; предупреждение (сообщение); } показатьСообщение(); // Привет, Джон
Функция имеет полный доступ к внешней переменной. Он также может изменить его.
Например:
пусть имя пользователя = 'Джон'; функция showMessage() { имя пользователя = "Боб"; // (1) изменил внешнюю переменную пусть сообщение = 'Привет' + имя пользователя; предупреждение (сообщение); } оповещение (имя пользователя); // Джон перед вызовом функции показатьСообщение(); оповещение (имя пользователя); // Боб, значение было изменено функцией
Внешняя переменная используется только в том случае, если локальной нет.
Если переменная с тем же именем объявлена внутри функции, она затеняет внешнюю. Например, в приведенном ниже коде функция использует локальное userName
. Внешний игнорируется:
пусть имя пользователя = 'Джон'; функция showMessage() { пусть имя пользователя = «Боб»; // объявляем локальную переменную пусть сообщение = 'Привет' + имя пользователя; // Боб предупреждение (сообщение); } // функция создаст и будет использовать собственное имя пользователя показатьСообщение(); оповещение (имя пользователя); // Джон, без изменений, функция не обращалась к внешней переменной
Глобальные переменные
Переменные, объявленные вне какой-либо функции, например внешнее userName
в приведенном выше коде, называются глобальными .
Глобальные переменные видны из любой функции (если они не затенены локальными элементами).
Хорошей практикой является минимизировать использование глобальных переменных. В современном коде мало глобальных переменных или они вообще отсутствуют. Большинство переменных находятся в своих функциях. Однако иногда они могут быть полезны для хранения данных уровня проекта.
Мы можем передавать произвольные данные функциям, используя параметры.
В примере ниже функция имеет два параметра: from
и text
.
function showMessage(from, text) { // параметры: from, text оповещение (от + ': ' + текст); } showMessage('Энн', 'Привет!'); // Энн: Привет! (*) showMessage('Энн', "Что случилось?"); // Энн: Что случилось? (**)
Когда функция вызывается в строках (*)
и (**)
, данные значения копируются в локальные переменные from
и text
. Затем функция их использует.
Вот еще один пример: у нас есть переменная from
и мы передаем ее в функцию. Обратите внимание: функция меняется from
, но снаружи это изменение не видно, поскольку функция всегда получает копию значения:
функция showMessage(from, text) { от = '*' + от + '*'; // сделать "от" более красивым предупреждение (из + ': ' + текст); } let from = "Энн"; showMessage(от, «Привет»); // *Энн*: Привет // значение "from" такое же, функция модифицировала локальную копию оповещение(от); // Энн
Когда значение передается как параметр функции, оно также называется аргументом .
Другими словами, если выразить эти термины прямо:
Параметр — это переменная, указанная в круглых скобках в объявлении функции (это термин времени объявления).
Аргумент — это значение, которое передается функции при ее вызове (это термин времени вызова).
Мы объявляем функции, перечисляя их параметры, а затем вызываем их, передавая аргументы.
В приведенном выше примере можно было бы сказать: «функция showMessage
объявляется с двумя параметрами, затем вызывается с двумя аргументами: from
и "Hello"
».
Если функция вызывается, но аргумент не указан, то соответствующее значение становится undefined
.
Например, вышеупомянутую функцию showMessage(from, text)
можно вызвать с одним аргументом:
showMessage("Энн");
Это не ошибка. Такой вызов выведет "*Ann*: undefined"
. Поскольку значение text
не передается, оно становится undefined
.
Мы можем указать так называемое значение «по умолчанию» (для использования, если оно опущено) для параметра в объявлении функции, используя =
:
function showMessage(from, text = "текст не указан") { предупреждение (от + ": " + текст); } showMessage("Энн"); // Энн: текст не указан
Теперь, если text
параметр не передан, он получит значение "no text given"
.
Значение по умолчанию также добавляется, если параметр существует, но строго равно undefined
, например:
showMessage("Энн", не определено); // Энн: текст не указан
Здесь "no text given"
— это строка, но это может быть более сложное выражение, которое оценивается и присваивается только в том случае, если параметр отсутствует. Итак, это также возможно:
функция showMessage(from, text =otherFunction()) { //otherFunction() выполняется только в том случае, если не указан текст // его результат становится значением текста }
Оценка параметров по умолчанию
В JavaScript параметр по умолчанию оценивается каждый раз, когда функция вызывается без соответствующего параметра.
В приведенном выше примере, anotherFunction()
вообще не вызывается, если указан text
параметр.
С другой стороны, он вызывается независимо каждый раз, когда text
отсутствует.
Параметры по умолчанию в старом коде JavaScript
Несколько лет назад JavaScript не поддерживал синтаксис параметров по умолчанию. Поэтому люди использовали другие способы их определения.
Сегодня мы можем встретить их в старых сценариях.
Например, явная проверка на undefined
:
функция showMessage(from, text) { если (текст === не определено) { text = 'текст не указан'; } предупреждение (от + ": " + текст); }
…Или используя ||
оператор:
функция showMessage(from, text) { // Если значение текста ложное, присвойте значение по умолчанию // предполагается, что text == "" — это то же самое, что отсутствие текста вообще текст = текст || 'текст не указан'; ... }
Иногда имеет смысл назначить значения по умолчанию для параметров на более позднем этапе после объявления функции.
Мы можем проверить, передается ли параметр во время выполнения функции, сравнив его с undefined
:
функция showMessage(текст) { // ... if (text === undefined) { // если параметр отсутствует текст = 'пустое сообщение'; } предупреждение (текст); } показатьСообщение(); // пустое сообщение
…Или мы могли бы использовать ||
оператор:
функция showMessage(текст) { // если текст не определен или иным образом неверен, установите для него значение «пусто» текст = текст || 'пустой'; ... }
Современные движки JavaScript поддерживают нулевой оператор объединения ??
лучше, если большинство ложных значений, таких как 0
, следует считать «нормальными»:
функция showCount(count) { // если счетчик не определен или равен нулю, показать «неизвестно» alert(количество ?? "неизвестно"); } шоуКаунт (0); // 0 showCount (ноль); // неизвестный шоуКаунт(); // неизвестный
В качестве результата функция может вернуть значение обратно в вызывающий код.
Простейшим примером может быть функция, которая суммирует два значения:
функция sum(a, b) { вернуть а + б; } пусть результат = сумма (1, 2); оповещение (результат); // 3
Директива return
может находиться в любом месте функции. Когда выполнение достигает этого, функция останавливается, и значение возвращается вызывающему коду (присвоенному result
выше).
В одной функции может быть много случаев return
. Например:
функция checkAge(возраст) { если (возраст >= 18) { вернуть истину; } еще { return submit('У вас есть разрешение от родителей?'); } } let age = Prompt('Сколько вам лет?', 18); если ( checkAge(возраст) ) { alert('Доступ предоставлен'); } еще { Предупреждение('Доступ запрещен'); }
return
можно использовать без значения. Это приводит к немедленному выходу из функции.
Например:
функция showMovie(возраст) { если ( !checkAge(возраст) ) { возвращаться; } alert("Показываю фильм"); // (*) // ... }
В приведенном выше коде, если checkAge(age)
возвращает false
, showMovie
не перейдет к alert
.
Функция с пустым return
или без него возвращает undefined
Если функция не возвращает значение, это то же самое, как если бы она возвращала undefined
:
function doNothing() { /* пусто */ } Предупреждение (doNothing() === не определено); // истинный
Пустой return
также аналогичен return undefined
:
функция делатьНичего() { возвращаться; } Предупреждение (doNothing() === не определено); // истинный
Никогда не добавляйте новую строку между return
и значением.
Для длинного выражения return
может возникнуть соблазн поместить его в отдельную строку, например:
возвращаться (некоторые + длинные + выражение + или + что угодно * f(a) + f(b))
Это не работает, потому что JavaScript предполагает точку с запятой после return
. Это будет работать так же, как:
возвращаться; (некоторые + длинные + выражение + или + что угодно * f(a) + f(b))
Таким образом, это фактически становится пустым возвратом.
Если мы хотим, чтобы возвращаемое выражение переносилось на несколько строк, мы должны начать его с той же строки, что и return
. Или хотя бы поставьте там открывающие скобки следующим образом:
возвращаться ( какое-то + длинное + выражение + или + что угодно * f(a) + f(b) )
И это будет работать так, как мы ожидаем.
Функции — это действия. Поэтому их имя обычно является глаголом. Он должен быть кратким, максимально точным и описывать, что делает функция, чтобы тот, кто читает код, получил представление о том, что делает функция.
Широко распространена практика начинать функцию с глагольного префикса, который в общих чертах описывает действие. Внутри команды должно быть соглашение о значении префиксов.
Например, функции, начинающиеся с "show"
обычно что-то показывают.
Функция, начинающаяся с…
"get…"
– вернуть значение,
"calc…"
– посчитать что-то,
"create…"
– создать что-либо,
"check…"
– проверить что-то и вернуть логическое значение и т. д.
Примеры таких названий:
showMessage(..) // показывает сообщение getAge(..) // возвращает возраст (как-то его получает) CalcSum(..) // вычисляет сумму и возвращает результат createForm(..) // создает форму (и обычно возвращает ее) checkPermission(..) // проверяет разрешение, возвращает true/false
При наличии префиксов взгляд на имя функции дает представление о том, какую работу она выполняет и какое значение возвращает.
Одна функция – одно действие
Функция должна делать именно то, что следует из ее названия, не более.
Два независимых действия обычно заслуживают двух функций, даже если они обычно вызываются вместе (в этом случае мы можем создать третью функцию, которая вызывает эти две).
Несколько примеров нарушения этого правила:
getAge
— было бы плохо, если бы отображалось alert
с возрастом (нужно только получить).
createForm
— было бы плохо, если бы он модифицировал документ, добавляя к нему форму (следует только создать ее и вернуть).
checkPermission
— было бы плохо, если бы отображалось сообщение access granted/denied
(должно только выполнять проверку и возвращать результат).
Эти примеры предполагают общие значения префиксов. Вы и ваша команда можете договориться о других значениях, но обычно они не сильно отличаются. В любом случае вы должны четко понимать, что означает префикс, что может и чего не может делать префиксная функция. Все функции с одинаковым префиксом должны подчиняться правилам. И команда должна делиться знаниями.
Ультракороткие имена функций
Функции, которые используются очень часто , иногда имеют сверхкороткие имена.
Например, среда jQuery определяет функцию с $
. В библиотеке Lodash есть основная функция с именем _
.
Это исключения. Обычно имена функций должны быть краткими и описательными.
Функции должны быть короткими и делать ровно одно действие. Если эта штука большая, возможно, стоит разделить функцию на несколько более мелких функций. Иногда следовать этому правилу может быть не так-то просто, но это определенно хорошо.
Отдельную функцию не только легче тестировать и отлаживать — само ее существование — отличный комментарий!
Например, сравните две функции showPrimes(n)
ниже. Каждый из них выводит простые числа до n
.
В первом варианте используется метка:
функция showPrimes(n) { nextPrime: for (пусть я = 2; я < n; я++) { for (пусть j = 2; j < i; j++) { если (i % j == 0) продолжить nextPrime; } предупреждение(я); // простое число } }
Во втором варианте используется дополнительная функция isPrime(n)
для проверки простоты:
функция showPrimes(n) { for (пусть я = 2; я < n; я++) { if (!isPrime(i)) продолжить; предупреждение (я); // простое число } } функция isPrime(n) { for (пусть я = 2; я < n; я++) { if ( n % i == 0) вернуть false; } вернуть истину; }
Второй вариант легче понять, не так ли? Вместо фрагмента кода мы видим имя действия ( isPrime
). Иногда люди называют такой код самоописывающим .
Таким образом, функции можно создавать, даже если мы не собираемся использовать их повторно. Они структурируют код и делают его читабельным.
Объявление функции выглядит следующим образом:
имя функции(параметры, разделители, запятая) { /* код */ }
Значения, передаваемые функции в качестве параметров, копируются в ее локальные переменные.
Функция может обращаться к внешним переменным. Но это работает только изнутри наружу. Код вне функции не видит ее локальные переменные.
Функция может возвращать значение. Если это не так, то его результат undefined
.
Чтобы сделать код понятным и понятным, рекомендуется использовать в функции преимущественно локальные переменные и параметры, а не внешние переменные.
Всегда легче понять функцию, которая получает параметры, работает с ними и возвращает результат, чем функцию, которая не получает параметров, но в качестве побочного эффекта изменяет внешние переменные.
Именование функции:
Имя должно четко описывать, что делает функция. Когда мы видим в коде вызов функции, хорошее имя мгновенно дает нам понимание того, что она делает и возвращает.
Функция — это действие, поэтому имена функций обычно словесные.
Существует множество известных префиксов функций, таких как create…
, show…
, get…
, check…
и так далее. Используйте их, чтобы подсказать, что делает функция.
Функции являются основными строительными блоками скриптов. Теперь мы рассмотрели основы и можем приступить к их созданию и использованию. Но это только начало пути. Мы собираемся возвращаться к ним еще много раз, углубляясь в их расширенные возможности.
важность: 4
Следующая функция возвращает true
если age
параметра больше 18
.
В противном случае он запрашивает подтверждение и возвращает результат:
функция checkAge(возраст) { если (возраст > 18) { вернуть истину; } еще { // ... return submit('Разрешили ли вам родители?'); } }
Будет ли функция работать по-другому, если else
будет удалено?
функция checkAge(возраст) { если (возраст > 18) { вернуть истину; } // ... return submit('Разрешили ли вам родители?'); }
Есть ли разница в поведении этих двух вариантов?
Никакой разницы!
В обоих случаях return confirm('Did parents allow you?')
выполняется именно тогда, когда условие if
ложно.
важность: 4
Следующая функция возвращает true
если age
параметра больше 18
.
В противном случае он запрашивает подтверждение и возвращает результат.
функция checkAge(возраст) { если (возраст > 18) { вернуть истину; } еще { return submit('Разрешили ли вам родители?'); } }
Перепишите его, чтобы выполнить то же самое, но без if
, в одной строке.
Сделайте два варианта checkAge
:
Используете оператор вопросительного знака ?
Использование ИЛИ ||
Использование оператора вопросительного знака '?'
:
функция checkAge(возраст) { возвращение (возраст > 18) ? true : submit('Разрешили ли вам родители?'); }
Использование ИЛИ ||
(самый короткий вариант):
функция checkAge(возраст) { возврат (возраст > 18 лет) || подтвердите('Родители разрешили Вам?'); }
Обратите внимание, что скобки вокруг age > 18
здесь не требуются. Они существуют для лучшей читаемости.
важность: 1
Напишите функцию min(a,b)
которая возвращает наименьшее из двух чисел a
и b
.
Например:
мин(2, 5) == 2 мин(3, -1) == -1 мин(1, 1) == 1
Решение, использующее if
:
функция мин(а, б) { если (а < б) { вернуть а; } еще { вернуть б; } }
Решение с оператором вопросительного знака '?'
:
функция мин(а, б) { вернуть а <б? а: б; }
PS В случае равенства a == b
не важно, что возвращать.
важность: 4
Напишите функцию pow(x,n)
которая возвращает x
в степени n
. Или, другими словами, умножает x
сам на себя n
раз и возвращает результат.
мощность(3, 2) = 3 * 3 = 9 pow(3, 3) = 3 * 3 * 3 = 27 pow(1, 100) = 1 * 1 * ...* 1 = 1
Создайте веб-страницу, которая запрашивает x
и n
, а затем показывает результат pow(x,n)
.
Запустить демо-версию
PS В этой задаче функция должна поддерживать только натуральные значения n
: целые числа от 1
.
функция pow(x, n) { пусть результат = х; for (пусть я = 1; я < n; я++) { результат *= х; } вернуть результат; } let x = Prompt("x?", ''); let n = Prompt("n?", ''); если (п < 1) { alert(`Power ${n} не поддерживается, используйте положительное целое число`); } еще { оповещение(pow(x, n)); }