Функции
— это блок программного кода на языке JavaScript, который определяется один раз и может выполняться, или вызываться, многократно. Возможно, вы уже знакомы с понятием «функция» под другим названием, таким как подпрограмма, или процедура. Функции могут иметь параметры: определение функции может включать список идентификаторов, которые называются параметрами и играют роль локальных переменных в теле функции.
При вызове функций им могут передаваться значения, или аргументы, соответствующие их параметрам. Функции часто используют свои аргументы для вычисления возвращаемого значения, которое является значением выражения вызова функции. В дополнение к аргументам при вызове любой функции ей передается еще одно значение, определяющее контекст вызова — значение в ключевом слове this.
Функции в языке JavaScript являются объектами и могут использоваться разными способами. Например, функции могут присваиваться переменным и передаваться другим функциям. Поскольку функции являются объектами, имеется возможность присваивать значения их свойствам и даже вызывать их методы.
В JavaScript допускается создавать определения функций, вложенные в другие функции, и такие функции будут иметь доступ ко всем переменным, присутствующим в области видимости определения.
Определение функций
Определение функции начинается с ключевого слова function, за которым указываются следующие компоненты:
Идентификатор, определяющий имя функции
Имя является обязательной частью инструкции объявления функции: оно будет использовано для создания новой переменной, которой будет присвоен объект новой функции. В выражениях определения функций имя может отсутствовать: при его наличии имя будет ссылаться на объект функции только в теле самой функции.
Пара круглых скобок вокруг списка из нуля или более идентификаторов, разделенных запятыми
Эти идентификаторы будут определять имена параметров функции и в теле функции могут использоваться как локальные переменные.
Пара фигурных скобок с нулем или более инструкций JavaScript внутри
Эти инструкции составляют тело функции: они выполняются при каждом вызове функции.
В следующем примере показано несколько определений функций в виде инструкций и выражений. Обратите внимание, что определения функций в виде выражений удобно использовать, только если они являются частью более крупных выражений, таких как присваивание или вызов функции, которые выполняют некоторые действия с помощью вновь объявленной функции:
// Выводит имена и значения всех свойств объекта obj function printprops(obj) < for(var p in obj) console.log(p + ": " + obj[p] + "\n"); >// Вычисляет расстояние между точками (x1,y1) и (x2,y2) function distance(x1, y1, x2, y2) < var dx = x2 - x1; var dy = y2 - y1; return Math.sqrt(dx*dx + dy*dy); >// Рекурсивная функция (вызывающая сама себя), вычисляющая факториал function factorial(x) < if (x // Следующее выражение определяет функцию, вычисляющую // квадрат аргумента. Обратите внимание, // что она присваивается переменной var square = function(x) < return x*x; >// Выражения определения функций могут иметь имена, что позволяет // производить рекурсивные вызовы var f = function fact(x) < if (x ; // Выражения определения функций иногда могут тут же вызываться: var tensquared = (function(x) (10));
Обратите внимание, что в выражениях определения функций имя функции может отсутствовать. Инструкция объявления функции фактически объявляет переменную и присваивает ей объект функции.
Выражение определения функции, напротив, не объявляет переменную. Однако в выражениях определения допускается указывать имя функции, как в функции вычисления факториала выше, которое может потребоваться в теле функции для вызова себя самой. Если выражение определения функции включает имя, данное имя будет ссылаться на объект функции в области видимости этой функции. Фактически имя функции становится локальной переменной, доступной только в теле функции. В большинстве случаев имя функции не требуется указывать в выражениях определения, что делает определения более компактными.
Обратите внимание, что большинство (но не все) функций в примере содержат инструкцию return. Инструкция return завершает выполнение функции и выполняет возврат значения своего выражения (если указано) вызывающей программе. Если выражение в инструкции return отсутствует, она возвращает значение undefined. Если инструкция return отсутствует в функции, интерпретатор просто выполнит все инструкции в теле функции и вернет вызывающей программе значение undefined.
Большинство функций в примере вычисляют некоторое значение, и в них инструкция return используется для возврата этого значения вызывающей программе. Функция printprops() несколько отличается в этом смысле: ее работа заключается в том, чтобы вывести имена свойств объекта. Ей не нужно возвращать какое-либо значение, поэтому в функции отсутствует инструкция return. Функция printprops() всегда будет возвращать значение undefined. (Функции, не имеющие возвращаемого значения, иногда называются процедурами.)
Вызов функций
Программный код, образующий тело функции, выполняется не в момент определения функции, а в момент ее вызова. Вызов функций выполняется с помощью выражения вызова. Выражение вызова состоит из выражения обращения к функции, которое возвращает объект функции, и следующими за ним круглыми скобками со списком из нуля или более выражений-аргументов, разделенных запятыми, внутри.
Если выражение обращения к функции является выражением обращения к свойству - если функция является свойством объекта или элементом массива (т.е. методом) - тогда выражение вызова является выражением вызова метода. В следующем фрагменте демонстрируется несколько примеров выражений вызова обычных функций:
printprops(); var d = distance(1,1,5,6); var f = factorial(5) / factorial(12); f = square(5);
При вызове функции вычисляются все выражения-аргументы (указанные между скобками), и полученные значения используются в качестве аргументов функции. Эти значения присваиваются параметрам, имена которых перечислены в определении функции. В теле функции выражения обращений к параметрам возвращают значения соответствующих аргументов.
При вызове обычной функции возвращаемое функцией значение становится значением выражения вызова. Если возврат из функции происходит по достижении ее конца интерпретатором, возвращается значение undefined. Если возврат из функции происходит в результате выполнения инструкции return, возвращается значение выражения, следующего за инструкцией return, или undefined, если инструкция return не имеет выражения.
Метод - это не что иное, как функция, которая хранится в виде свойства объекта. Если имеется функция func и объект obj, то можно определить метод объекта obj с именем method, как показано ниже:
// Определим простой объект и функцию var obj = <>; function func(a, b) < return a+b;>// Добавим в объект obj метод obj.method = func; // Теперь можно вызвать этот метод var result = obj.method(4, 5);
Чаще всего при вызове методов используется форма обращения к свойствам с помощью оператора точки, однако точно так же можно использовать форму обращения к свойствам с помощью квадратных скобок. Например, оба следующих выражения являются выражениями вызова методов:
result = obj.method(4, 5); result = obj['method'](4, 5);
Аргументы и возвращаемое значение при вызове метода обрабатываются точно так же, как при вызове обычной функции. Однако вызов метода имеет одно важное отличие: контекст вызова. Выражение обращения к свойству состоит из двух частей: объекта (в данном случае obj) и имени свойства (method). В подобных выражениях вызова методов объект obj становится контекстом вызова, и тело функции получает возможность ссылаться на этот объект с помощью ключевого слова this. Например:
var obj = < x: 0, y: 0, // Метод add: function(a, b) < this.x = a; this.y = b; >, // Еще один метод sum: function() < return this.x + this.y >>; // Вызов методов obj.add(15, 4); console.log(obj.sum()); // 19
Методы и ключевое слово this занимают центральное место в парадигме объектно-ориентированного программирования. Любая функция, используемая как метод, фактически получает неявный аргумент - объект, относительно которого она была вызвана. Как правило, методы выполняют некоторые действия с объектом, и синтаксис вызова метода наглядно отражает тот факт, что функция оперирует объектом.
Обратите внимание: this - это именно ключевое слово, а не имя переменной или свойства. Синтаксис JavaScript не допускает возможность присваивания значений элементу this.
Аргументы и параметры функций
В языке JavaScript, в определениях функций не указываются типы параметров, а при вызове функций не выполняется никаких проверок типов передаваемых значений аргументов. Фактически при вызове функций в языке JavaScript не проверяется даже количество аргументов. В подразделах ниже описывается, что происходит, если число аргументов в вызове функции меньше или больше числа объявленных параметров. В них также демонстрируется, как можно явно проверить типы аргументов функции, если необходимо гарантировать, что функция не будет вызвана с некорректными аргументами.
Необязательные аргументы
Когда число аргументов в вызове функции меньше числа объявленных параметров, недостающие аргументы получают значение undefined. Часто бывает удобным писать функции так, чтобы некоторые аргументы были необязательными и могли опускаться при вызове функции. В этом случае желательно предусмотреть возможность присваивания достаточно разумных значений по умолчанию параметрам, которые могут быть опущены. Например:
// Добавить в массив arr перечислимые имена // свойств объекта obj и вернуть его. Если аргумент // arr не не был передан, создать и вернуть новый массив function getPropertyNames(obj, /* необязательный */ arr) < if (arr === undefined) arr = []; // Если массив не определен, создать новый for(var property in obj) arr.push(property); return arr; >// Эта функция может вызываться с 1 или 2 аргументами: var a = getPropertyNames(); // Получить свойства объекта в новом массиве getPropertyNames(,a); // добавить свойства нового объекта в этот массив console.log(a); // ['x', 'y', 'z']
Обратите внимание, что при объявлении функций необязательные аргументы должны завершать список аргументов, чтобы их можно было опустить. Программист, который будет писать обращение к вашей функции, не сможет передать второй аргумент и при этом опустить первый: он будет вынужден явно передать в первом аргументе значение undefined. Обратите также внимание на комментарий /* необязательный */ в определении функции, который подчеркивает тот факт, что параметр является необязательным.
Списки аргументов переменной длины
Если число аргументов в вызове функции превышает число имен параметров, функция лишается возможности напрямую обращаться к неименованным значениям. Решение этой проблемы предоставляет объект Arguments. В теле функции идентификатор arguments ссылается на объект Arguments, присутствующий в вызове. Объект Arguments - это объект, подобный массиву, позволяющий извлекать переданные функции значения по их номерам, а не по именам.
Предположим, что была определена функция func, которая требует один аргумент x. Если вызвать эту функцию с двумя аргументами, то первый будет доступен внутри функции по имени параметра x или как arguments[0]. Второй аргумент будет доступен только как arguments[1]. Кроме того, подобно настоящим массивам, arguments имеет свойство length, определяющее количество содержащихся элементов. То есть в теле функции func, вызываемой с двумя аргументами, arguments.length имеет значение 2.
Объект Arguments может использоваться с самыми разными целями. Следующий пример показывает, как с его помощью проверить, была ли функция вызвана с правильным числом аргументов, - ведь JavaScript этого за вас не сделает:
function func(x, y, z) < // Сначала проверяется, правильное ли количество аргументов передано if (arguments.length != 3) < throw new Error("Функция func вызвана с " + arguments.length + " аргументами, а требуется 3."); >// А теперь сам код функции. >
Обратите внимание, что зачастую нет необходимости проверять количество аргументов, как в данном примере. Поведение по умолчанию интерпретатора JavaScript отлично подходит для большинства случаев: отсутствующие аргументы замещаются значением undefined, а лишние аргументы просто игнорируются.
Объект Arguments иллюстрирует важную возможность JavaScript-функций: они могут быть написаны таким образом, чтобы работать с любым количеством аргументов. Следующая функция принимает любое число аргументов и возвращает значение самого большого из них (аналогично ведет себя встроенная функция Math.max()):
function maxNumber() < var m = Number.NEGATIVE_INFINITY; // Цикл по всем аргументам, поиск и // сохранение наибольшего из них for(var i = 0; i < arguments.length; i++) if (arguments[i] >m) m = arguments[i]; // Вернуть наибольшее значение return m; > var largest = maxNumber(1, 10, 100, 2, 3, 1000, 4, 5, 10000, 6); // 10000
Функции, подобные этой и способные принимать произвольное число аргументов, называются . Этот термин возник вместе с появлением языка программирования C.
Обратите внимание, что функции с переменным числом аргументов не должны допускать возможность вызова с пустым списком аргументов. Будет вполне разумным использовать объект arguments[] при написании функции, ожидающей получить фиксированное число обязательных именованных аргументов, за которыми может следовать произвольное число необязательных неименованных аргументов.
Не следует забывать, что arguments фактически не является массивом - это объект Arguments. В каждом объекте Arguments имеются пронумерованные элементы массива и свойство length, но с технической точки зрения это не массив. Лучше рассматривать его как объект, имеющий некоторые пронумерованные свойства.
Помимо элементов своего массива объект Arguments определяет свойства callee и caller. При попытке изменить значения этих свойств в строгом режиме ECMAScript 5 гарантированно возбуждается исключение TypeError. Однако в нестрогом режиме стандарт ECMAScript утверждает, что свойство callee ссылается на выполняемую в данный момент функцию. Свойство caller не является стандартным, но оно присутствует во многих реализациях и ссылается на функцию, вызвавшую текущую.
Свойство caller можно использовать для доступа к стеку вызовов, а свойство callee особенно удобно использовать для рекурсивного вызова неименованных функций:
var factorial = function(x) < if (x ;
Свойства и методы функций
Мы видели, что в JavaScript-программах функции могут использоваться как значения. Оператор typeof возвращает для функций строку «function», однако в действительности функции в языке JavaScript - это особого рода объекты. А раз функции являются объектами, то они имеют свойства и методы, как любые другие объекты. Существует даже конструктор Function(), который создает новые объекты функций. В следующих подразделах описываются свойства и методы функций.
Свойство length
В теле функции свойство arguments.length определяет количество аргументов, переданных функции. Однако свойство length самой функции имеет иной смысл. Это свойство, доступное только для чтения, возвращает количество аргументов, которое функция ожидает получить - число объявленных параметров.
В следующем фрагменте определяется функция с именем check(), получающая массив аргументов arguments от другой функции. Она сравнивает свойство arguments.length (число фактически переданных аргументов) со свойством arguments.callee.length (число ожидаемых аргументов), чтобы определить, передано ли функции столько аргументов, сколько она ожидает. Если значения не совпадают, генерируется исключение. За функцией check() следует тестовая функция func(), демонстрирующая порядок использования функции check():
// Эта функция использует arguments.callee, поэтому она // не будет работать в строгом режиме function check(args) < var actual = args.length; // Фактическое число аргументов var expected = args.callee.length; // Ожидаемое число аргументов if (actual !== expected) // Если не совпадают, генерируется исключение throw new Error("ожидается: " + expected + "; получено " + actual); >function func(x, y, z) < // Проверить число ожидаемых и фактически переданных аргументов check(arguments); // Теперь выполнить оставшуюся часть функции return x + y + z; >
Свойство prototype
Любая функция имеет свойство prototype, ссылающееся на объект, известный как объект прототипа. Каждая функция имеет свой объект прототипа. Когда функция используется в роли конструктора, вновь созданный объект наследует свойства этого объекта прототипа.
Прототипы и свойство prototype обсуждались в предыдущей статье.
Методы call() и apply()
Методы call() и apply() позволяют выполнять косвенный вызов функции, как если бы она была методом некоторого другого объекта. Первым аргументом обоим методам, call() и apply(), передается объект, относительно которого вызывается функция; этот аргумент определяет контекст вызова и становится значением ключевого слова this в теле функции. Чтобы вызвать функцию func() (без аргументов) как метод объекта obj, можно использовать любым из методов, call() или apply():
func.call(obj); func.apply(obj);
Любой из этих способов вызова эквивалентен следующему фрагменту (где предполагается, что объект obj не имеет свойства с именем m):
obj.m = func; // Временно сделать func методом obj obj.m(); // Вызывать его без аргументов. delete obj.m; // Удалить временный метод.
В строгом режиме ECMAScript 5 первый аргумент методов call() и apply() становится значением this, даже если это простое значение, null или undefined. В ECMAScript 3 и в нестрогом режиме значения null и undefined замещаются глобальным объектом, а простое значение - соответствующим объектом-оберткой.
Все остальные аргументы метода call(), следующие за первым аргументом, определяющим контекст вызова, передаются вызываемой функции. Метод apply() действует подобно методу call(), за исключением того, что аргументы для функции передаются в виде массива. Если функция способна обрабатывать произвольное число аргументов, метод apply() может использоваться для вызова такой функции в контексте массива произвольной длины.
В следующем примере демонстрируется практическое применение метода call():
// Ниже определены две функции, отображающие свойства и // значения свойств произвольного объекта. Способ // отображения передаются в виде аргумента func function print1(func, obj) < for (n in obj) func(n +': ' + obj[n]); >function print2(func, objDevice, obj) < for (n in obj) func.call(objDevice, n +': ' + obj[n]); >var obj = ; print2(document.write, document, obj); // Работает корректно print2(console.log, console, obj); print1(document.write, obj); // Возникнет исключение Illegal invocation, т.к. print1(console.log, obj); // невозможно вызвать эти методы без объекта контекста
Метод bind()
Метод bind() впервые появился в ECMAScript 5, но его легко имитировать в ECMAScript 3. Как следует из его имени, основное назначение метода bind() состоит в том, чтобы связать (bind) функцию с объектом. Если вызвать метод bind() функции func и передать ему объект obj, он вернет новую функцию. Вызов новой функции (как обычной функции) выполнит вызов оригинальной функции func как метода объекта obj. Любые аргументы, переданные новой функции, будут переданы оригинальной функции. Например:
// Функция, которую требуется привязать function func(y) < return this.x + y; >var obj = ; // Объект, к которому выполняется привязка var g = func.bind(obj); // Вызов g(x) вызовет obj.func(x)
Такой способ связывания легко реализовать в ECMAScript 3, как показано ниже:
// Возвращает функцию, которая вызывает func как метод объекта obj // и передает ей все свои аргументы function bind(func, obj) < if (func.bind) return func.bind(obj); // Использовать метод bind, если имеется else return function() < // Иначе связать, как показано ниже return func.apply(obj, arguments); >; >
Метод bind() в ECMAScript 5 не просто связывает функцию с объектом. Он также выполняет частичное применение: помимо значения this связаны будут все аргументы, переданные методу bind() после первого его аргумента. Частичное применение - распространенный прием в функциональном программировании и иногда называется каррингом (currying).
JavaScript Функции
JavaScript функция - это блок кода, предназначенный для выполнения определенной задачи.
JavaScript функция выполняется, когда ее вызывают (вызвает) "что-то".
Пример
function myFunction(p1, p2) <
return p1 * p2; // Функция возвращает произведение p1 и p2
>
JavaScript Синтаксис функции
JavaScript функция определяется с помощью ключевого кода function , за которым следует имя и скобки ().
Имена функций могут содержать буквы, цифры, подчеркивания и знаки доллара (те же правила, что и для переменных).
Скобки могут включать имена параметров, разделенные запятыми:
(parameter1, parameter2, . )
Код, который будет выполнен функцией, помещается в фигурные скобки: <>
function name(parameter1, parameter2, parameter3) // код для выполнения
>
Параметры функции перечислены в скобках () в определении функции.
Аргументы функции - это значения , получаемые функцией при ее вызове.
Внутри функции аргументы (параметры) действуют как локальные переменные.
Функция во многом похожа на процедуру или подпрограмму на других языках программирования.
JavaScript Вызов функции
Код внутри функции будет выполняться, когда "что-то" вызывает (вызывается) функцию:
- Когда происходит событие (когда пользователь нажимает кнопку)
- Когда он вызывает (вызывается) из JavaScript кода
- Автоматически (вызывается самостоятельно)
Вы узнаете гораздо больше о вызове функций позже в этом руководстве.
JavaScript Возврат функции
Когда, JavaScript достигает оператора return , функция прекращает выполнение.
Если функция была вызвана оператором, JavaScript "вернет" выполнение кода после вызова оператора.
Функции всегда вычисляют возвращаемое значение. Возвращаемое значение "возвращается" обратно "вызывающему":
Пример
Вычислите произведение двух чисел и верните результат:
var x = myFunction(4, 3); // Функция вызывается, возвращаемое значение будет в конечном итоге в x
function myFunction(a, b) return a * b; // Функция возвращает произведение a и b
>
Результат в x будет:
Почему функции?
Вы можете повторно использовать код: определить код один раз и использовать его несколько раз.
Вы можете использовать один и тот же код много раз, с разными аргументами для получения разных результатов.
Пример
Преобразовать Фаренгейты в Цельсия:
function toCelsius(fahrenheit) <
return (5/9) * (fahrenheit-32);
>
document.getElementById("demo").innerHTML = toCelsius(77);
Оператор () вызывает функцию
В приведенном выше примере toCelsius ссылка на объект функции и toCelsius() ссылка на результат функции.
Доступ к функции без () вернет объект функции вместо результата функции.
Пример
function toCelsius(fahrenheit) <
return (5/9) * (fahrenheit-32);
>
document.getElementById("demo").innerHTML = toCelsius;
Функции, используемые как значения переменных
Функции можно использовать так же, как и переменные, во всех типах формул, назначений и вычислений.
Пример
Вместо использования переменной для хранения возвращаемого значения функции:
var x = toCelsius(77);
var text = "Температура " + x + " по цельсию";
Вы можете использовать функцию напрямую, как значение переменной:
var text = "Температура " + toCelsius(77) + " по цельсию";
Позже в этом руководстве вы узнаете гораздо больше о функциях.
Локальные переменные
Переменные, объявленные в JavaScript функции, становятся ЛОКАЛЬНЫМИ для функции.
Доступ к локальным переменным возможен только из функции.
Пример
// код здесь, нельзя использовать carName
function myFunction() var carName = "Вольво";
// код здесь, можно использовать carName
>
// код здесь, нельзя использовать carName
Поскольку локальные переменные распознаются только внутри своих функций, переменные с одинаковыми именами могут использоваться в разных функциях.
Локальные переменные создаются при запуске функции и удаляются, когда функция завершается.
Мы только что запустили
SchoolsW3 видео
ВЫБОР ЦВЕТА
курс сегодня!
Сообщить об ошибке
Если вы хотите сообщить об ошибке или внести предложение, не стесняйтесь отправлять на электронное письмо:
Ваше предложение:
Спасибо Вам за то, что помогаете!
Ваше сообщение было отправлено в SchoolsW3.
ТОП Учебники
ТОП Справочники
ТОП Примеры
Получить сертификат
SchoolsW3 оптимизирован для бесплатного обучения, проверки и подготовки знаний. Примеры в редакторе упрощают и улучшают чтение и базовое понимание. Учебники, ссылки, примеры постоянно пересматриваются, чтобы избежать ошибок, но не возможно гарантировать полную правильность всего содержания. Некоторые страницы сайта могут быть не переведены на РУССКИЙ язык, можно отправить страницу как ошибку, так же можете самостоятельно заняться переводом. Используя данный сайт, вы соглашаетесь прочитать и принять Условия к использованию, Cookies и политика конфиденциальности.
Scope переменных
Scope или область видимости относится к доступности переменных и функций в определенных частях кода.
В JavaScript переменная имеет два типа области видимости:
Глобальный Scope
Переменная, объявленная в верхней части программы или вне функции, считается переменной глобальной области видимости.
Рассмотрим пример переменной глобальной области видимости:
// программа для вывода текста let a = 'привет'; function greet() < console.log(a); >greet(); // привет
В приведенной выше программе переменная a объявлена в верхней части программы и является глобальной переменной. Это означает, что переменная a может быть использована в любом месте программы.
Значение глобальной переменной может быть изменено внутри функции. Например,
// программа, чтобы показать изменение глобальной переменной let a = 'привет'; function greet() < a = 3; >// до вызова функции console.log(a); // после вызова функции greet(); console.log(a); // 3
В приведенной выше программе переменная a является глобальной переменной. Значение переменной a равно привет . Затем к переменной a обращаются внутри функции, и ее значение меняется на 3.
Следовательно, значение a меняется после его изменения внутри функции.
Примечание: Следует избегать использования глобальных переменных, поскольку значение глобальной переменной может изменяться в различных областях программы. Это может привести к неожиданным результатам в программе.
В JavaScript переменную можно использовать и без ее объявления. Если переменная используется без объявления, она автоматически становится глобальной переменной.
function greet() < a = 'привет'; >greet(); console.log(a); // привет
В приведенной выше программе переменная a является глобальной переменной.
Если бы переменная была объявлена с помощью let a = "hello" , программа выдала бы ошибку.
Примечание: В JavaScript существует "strict mode"; , в котором переменная не может быть использована без ее объявления.
локальный Scope
Переменная также может иметь локальную область видимости, то есть доступ к ней возможен только внутри функции.
Пример 1: Переменная локальной области
// программа, показывающая локальную область видимости переменной let a = 'привет, '; function greet() < let b = 'мир'; console.log(a + b); >greet(); console.log(a + b); // ошибка
Вывод в консоль:
привет, мир Uncaught ReferenceError: b is not defined
В приведенной выше программе переменная a является глобальной переменной, а переменная b - локальной переменной. Доступ к переменной b возможен только внутри функции greet . Поэтому, когда мы пытаемся получить доступ к переменной b вне функции, возникает ошибка.
let имеет блочный scope
Ключевое слово let имеет блочную область видимости. Доступ к переменной возможен только в непосредственном блоке.
Пример 3: Блочная область видимости переменной
// программа, показывающая концепцию блочной области видимости // глобальная переменная let a = 'Привет'; function greet() < // локальная переменная let b = 'Мир'; console.log(a + ' ' + b); if (b == 'Мир') < // переменная блочной видимости let c = 'привет'; console.log(a + ' ' + b + ' ' + c); >// переменная c здесь недоступна console.log(a + ' ' + b + ' ' + c); > greet();
Вывод в консоль:
Привет Мир Привет Мир привет Uncaught ReferenceError: c is not defined
В приведенной выше программе переменная:
- a
- это глобальная переменная. К ней можно обратиться в любом месте программы.
- это локальная переменная. Доступ к ней возможен только внутри функции greet .
- это переменная блочной scope. Доступ к ней возможен только внутри блока оператора if .
Поэтому в приведенной выше программе первые два console.log() работают без проблем.
Однако, когда мы пытаемся получить доступ к переменной c , с блочной scope, вне блока в третьей строке console.log() . Это приводит к ошибке.
Примечание: В JavaScript переменная var относится к функциональной scope, а let - к блочной scope. Если попытаться использовать var c = 'hello'; внутри оператора if в приведенной выше программе, вся программа будет работать, так как c будет рассматриваться как локальная переменная.
Функции
Зачастую нам надо повторять одно и то же действие во многих частях программы.
Например, необходимо красиво вывести сообщение при приветствии посетителя, при выходе посетителя с сайта, ещё где-нибудь.
Чтобы не повторять один и тот же код во многих местах, придуманы функции. Функции являются основными «строительными блоками» программы.
Примеры встроенных функций вы уже видели – это alert(message) , prompt(message, default) и confirm(question) . Но можно создавать и свои.
Объявление функции
Для создания функций мы можем использовать объявление функции.
Пример объявления функции:
function showMessage()
Вначале идёт ключевое слово function , после него имя функции, затем список параметров в круглых скобках через запятую (в вышеприведённом примере он пустой) и, наконец, код функции, также называемый «телом функции», внутри фигурных скобок.
function имя(параметры)
Наша новая функция может быть вызвана по своему имени: showMessage() .
function showMessage() < alert( 'Всем привет!' ); >showMessage(); showMessage();
Вызов showMessage() выполняет код функции. Здесь мы увидим сообщение дважды.
Этот пример явно демонстрирует одно из главных предназначений функций: избавление от дублирования кода.
Если понадобится поменять сообщение или способ его вывода – достаточно изменить его в одном месте: в функции, которая его выводит.
Локальные переменные
Переменные, объявленные внутри функции, видны только внутри этой функции.
function showMessage() < let message = "Привет, я JavaScript!"; // локальная переменная alert( message ); >showMessage(); // Привет, я JavaScript! alert( message ); //
Внешние переменные
У функции есть доступ к внешним переменным, например:
let userName = 'Вася'; function showMessage() < let message = 'Привет, ' + userName; alert(message); >showMessage(); // Привет, Вася
Функция обладает полным доступом к внешним переменным и может изменять их значение.
let userName = 'Вася'; function showMessage() < userName = "Петя"; // (1) изменяем значение внешней переменной let message = 'Привет, ' + userName; alert(message); >alert( userName ); // Вася перед вызовом функции showMessage(); alert( userName ); // Петя, значение внешней переменной было изменено функцией
Внешняя переменная используется, только если внутри функции нет такой локальной.
Если одноимённая переменная объявляется внутри функции, тогда она перекрывает внешнюю. Например, в коде ниже функция использует локальную переменную userName . Внешняя будет проигнорирована:
let userName = 'Вася'; function showMessage() < let userName = "Петя"; // объявляем локальную переменную let message = 'Привет, ' + userName; // Петя alert(message); >// функция создаст и будет использовать свою собственную локальную переменную userName showMessage(); alert( userName ); // Вася, не изменилась, функция не трогала внешнюю переменную
Глобальные переменные
Переменные, объявленные снаружи всех функций, такие как внешняя переменная userName в вышеприведённом коде – называются глобальными.
Глобальные переменные видимы для любой функции (если только их не перекрывают одноимённые локальные переменные).
Желательно сводить использование глобальных переменных к минимуму. В современном коде обычно мало или совсем нет глобальных переменных. Хотя они иногда полезны для хранения важнейших «общепроектовых» данных.
Параметры
Мы можем передать внутрь функции любую информацию, используя параметры.
В нижеприведённом примере функции передаются два параметра: from и text .
function showMessage(from, text) < // параметры: from, text alert(from + ': ' + text); >showMessage('Аня', 'Привет!'); // Аня: Привет! (*) showMessage('Аня', "Как дела?"); // Аня: Как дела? (**)
Когда функция вызывается в строках (*) и (**) , переданные значения копируются в локальные переменные from и text . Затем они используются в теле функции.
Вот ещё один пример: у нас есть переменная from , и мы передаём её функции. Обратите внимание: функция изменяет значение from , но это изменение не видно снаружи. Функция всегда получает только копию значения:
function showMessage(from, text) < from = '*' + from + '*'; // немного украсим "from" alert( from + ': ' + text ); >let from = "Аня"; showMessage(from, "Привет"); // *Аня*: Привет // значение "from" осталось прежним, функция изменила значение локальной переменной alert( from ); // Аня
Значение, передаваемое в качестве параметра функции, также называется аргументом.
- Параметр – это переменная, указанная в круглых скобках в объявлении функции.
- Аргумент – это значение, которое передаётся функции при её вызове.
Мы объявляем функции со списком параметров, затем вызываем их, передавая аргументы.
Рассматривая приведённый выше пример, мы могли бы сказать: "функция showMessage объявляется с двумя параметрами, затем вызывается с двумя аргументами: from и "Привет" ".
Значения по умолчанию
Если при вызове функции аргумент не был указан, то его значением становится undefined .
Например, вышеупомянутая функция showMessage(from, text) может быть вызвана с одним аргументом:
showMessage("Аня");
Это не приведёт к ошибке. Такой вызов выведет "*Аня*: undefined" . В вызове не указан параметр text , поэтому предполагается, что text === undefined .
Если мы хотим задать параметру text значение по умолчанию, мы должны указать его после = :
function showMessage(from, text = "текст не добавлен") < alert( from + ": " + text ); >showMessage("Аня"); // Аня: текст не добавлен
Теперь, если параметр text не указан, его значением будет "текст не добавлен"
В данном случае "текст не добавлен" это строка, но на её месте могло бы быть и более сложное выражение, которое бы вычислялось и присваивалось при отсутствии параметра. Например:
function showMessage(from, text = anotherFunction()) < // anotherFunction() выполнится только если не передан text // результатом будет значение text >
Вычисление параметров по умолчанию
В JavaScript параметры по умолчанию вычисляются каждый раз, когда функция вызывается без соответствующего параметра.
В приведённом выше примере, функция anotherFunction() не будет вызвана вообще, если указан параметр text .
С другой стороны, функция будет независимо вызываться каждый раз, когда text отсутствует.
Использование параметров по умолчанию в ранних версиях JavaScript
Ранние версии JavaScript не поддерживали параметры по умолчанию. Поэтому существуют альтернативные способы, которые могут встречаться в старых скриптах.
Например, явная проверка на undefined :
function showMessage(from, text) < if (text === undefined) < text = 'текст не добавлен'; >alert( from + ": " + text ); >
…Или с помощью оператора || :
function showMessage(from, text) < // Если значение text ложно, тогда присвоить параметру text значение по умолчанию // заметим, что при этом пустая строка text === "" будет также считаться отсутствующим значением text = text || 'текст не добавлен'; . >
Альтернативные параметры по умолчанию
Иногда имеет смысл присваивать значения по умолчанию для параметров не в объявлении функции, а на более позднем этапе.
Во время выполнения функции мы можем проверить, передан ли параметр, сравнив его с undefined :
function showMessage(text) < // . if (text === undefined) < // если параметр отсутствует text = 'пустое сообщение'; >alert(text); > showMessage(); // пустое сообщение
…Или мы можем использовать оператор || :
function showMessage(text) < // если значение text ложно или равняется undefined, тогда присвоить text значение 'пусто' text = text || 'пусто'; . >
Современные движки JavaScript поддерживают оператор нулевого слияния ?? . Его использование будет лучшей практикой, в случае, если большинство ложных значений, таких как 0 , следует расценивать как «нормальные».
function showCount(count) < // если count равен undefined или null, показать "неизвестно" alert(count ?? "неизвестно"); >showCount(0); // 0 showCount(null); // неизвестно showCount(); // неизвестно
Возврат значения
Функция может вернуть результат, который будет передан в вызвавший её код.
Простейшим примером может служить функция сложения двух чисел:
function sum(a, b) < return a + b; >let result = sum(1, 2); alert( result ); // 3
Директива return может находиться в любом месте тела функции. Как только выполнение доходит до этого места, функция останавливается, и значение возвращается в вызвавший её код (присваивается переменной result выше).
Вызовов return может быть несколько, например:
function checkAge(age) < if (age >= 18) < return true; >else < return confirm('А родители разрешили?'); >> let age = prompt('Сколько вам лет?', 18); if ( checkAge(age) ) < alert( 'Доступ получен' ); >else
Возможно использовать return и без значения. Это приведёт к немедленному выходу из функции.
function showMovie(age) < if ( !checkAge(age) ) < return; >alert( "Вам показывается кино" ); // (*) // . >
В коде выше, если checkAge(age) вернёт false , showMovie не выполнит alert .
Результат функции с пустым return или без него – undefined
Если функция не возвращает значения, это всё равно, как если бы она возвращала undefined :
function doNothing() < /* пусто */ >alert( doNothing() === undefined ); // true
Пустой return аналогичен return undefined :
function doNothing() < return; >alert( doNothing() === undefined ); // true
Никогда не добавляйте перевод строки между return и его значением
Для длинного выражения в return может быть заманчиво разместить его на нескольких отдельных строках, например так:
return (some + long + expression + or + whatever * f(a) + f(b))
Код не выполнится, потому что интерпретатор JavaScript подставит точку с запятой после return . Для него это будет выглядеть так:
return; (some + long + expression + or + whatever * f(a) + f(b))
Таким образом, это фактически стало пустым return .
Если мы хотим, чтобы возвращаемое выражение занимало несколько строк, нужно начать его на той же строке, что и return . Или, хотя бы, поставить там открывающую скобку, вот так:
return ( some + long + expression + or + whatever * f(a) + f(b) )
И тогда всё сработает, как задумано.
Выбор имени функции
Функция – это действие. Поэтому имя функции обычно является глаголом. Оно должно быть кратким, точным и описывать действие функции, чтобы программист, который будет читать код, получил верное представление о том, что делает функция.
Как правило, используются глагольные префиксы, обозначающие общий характер действия, после которых следует уточнение. Обычно в командах разработчиков действуют соглашения, касающиеся значений этих префиксов.
Например, функции, начинающиеся с "show" обычно что-то показывают.
Функции, начинающиеся с…
- "get…" – возвращают значение,
- "calc…" – что-то вычисляют,
- "create…" – что-то создают,
- "check…" – что-то проверяют и возвращают логическое значение, и т.д.
Примеры таких имён:
showMessage(..) // показывает сообщение getAge(..) // возвращает возраст (получая его каким-то образом) calcSum(..) // вычисляет сумму и возвращает результат createForm(..) // создаёт форму (и обычно возвращает её) checkPermission(..) // проверяет доступ, возвращая true/false
Благодаря префиксам, при первом взгляде на имя функции становится понятным, что делает её код, и какое значение она может возвращать.
Одна функция – одно действие
Функция должна делать только то, что явно подразумевается её названием. И это должно быть одним действием.
Два независимых действия обычно подразумевают две функции, даже если предполагается, что они будут вызываться вместе (в этом случае мы можем создать третью функцию, которая будет их вызывать).
Несколько примеров, которые нарушают это правило:
- getAge – будет плохим выбором, если функция будет выводить alert с возрастом (должна только возвращать его).
- createForm – будет плохим выбором, если функция будет изменять документ, добавляя форму в него (должна только создавать форму и возвращать её).
- checkPermission – будет плохим выбором, если функция будет отображать сообщение с текстом доступ разрешён/запрещён (должна только выполнять проверку и возвращать её результат).
В этих примерах использовались общепринятые смыслы префиксов. Конечно, вы в команде можете договориться о других значениях, но обычно они мало отличаются от общепринятых. В любом случае вы и ваша команда должны чётко понимать, что значит префикс, что функция с ним может делать, а чего не может.
Сверхкороткие имена функций
Имена функций, которые используются очень часто, иногда делают сверхкороткими.
Например, фреймворк jQuery определяет функцию с помощью $ . В библиотеке Lodash основная функция представлена именем _ .
Это исключения. В основном имена функций должны быть в меру краткими и описательными.
Функции == Комментарии
Функции должны быть короткими и делать только что-то одно. Если это что-то большое, имеет смысл разбить функцию на несколько меньших. Иногда следовать этому правилу непросто, но это определённо хорошее правило.
Небольшие функции не только облегчают тестирование и отладку – само существование таких функций выполняет роль хороших комментариев!
Например, сравним ниже две функции showPrimes(n) . Каждая из них выводит простое число до n .
Первый вариант использует метку nextPrime :
function showPrimes(n) < nextPrime: for (let i = 2; i < n; i++) < for (let j = 2; j < i; j++) < if (i % j == 0) continue nextPrime; >alert( i ); // простое > >
Второй вариант использует дополнительную функцию isPrime(n) для проверки на простое:
function showPrimes(n) < for (let i = 2; i < n; i++) < if (!isPrime(i)) continue; alert(i); // простое >> function isPrime(n) < for (let i = 2; i < n; i++) < if ( n % i == 0) return false; >return true; >
Второй вариант легче для понимания, не правда ли? Вместо куска кода мы видим название действия ( isPrime ). Иногда разработчики называют такой код самодокументируемым.
Таким образом, допустимо создавать функции, даже если мы не планируем повторно использовать их. Такие функции структурируют код и делают его более понятным.
Итого
Объявление функции имеет вид:
function имя(параметры, через, запятую) < /* тело, код функции */ >
- Передаваемые значения копируются в параметры функции и становятся локальными переменными.
- Функции имеют доступ к внешним переменным. Но это работает только изнутри наружу. Код вне функции не имеет доступа к её локальным переменным.
- Функция может возвращать значение. Если этого не происходит, тогда результат равен undefined .
Для того, чтобы сделать код более чистым и понятным, рекомендуется использовать локальные переменные и параметры функций, не пользоваться внешними переменными.
Функция, которая получает параметры, работает с ними и затем возвращает результат, гораздо понятнее функции, вызываемой без параметров, но изменяющей внешние переменные, что чревато побочными эффектами.
- Имя функции должно понятно и чётко отражать, что она делает. Увидев её вызов в коде, вы должны тут же понимать, что она делает, и что возвращает.
- Функция – это действие, поэтому её имя обычно является глаголом.
- Есть много общепринятых префиксов, таких как: create… , show… , get… , check… и т.д. Пользуйтесь ими как подсказками, поясняющими, что делает функция.
Функции являются основными строительными блоками скриптов. Мы рассмотрели лишь основы функций в JavaScript, но уже сейчас можем создавать и использовать их. Это только начало пути. Мы будем неоднократно возвращаться к функциям и изучать их всё более и более глубоко.
Задачи
Обязателен ли "else"?
важность: 4
Следующая функция возвращает true , если параметр age больше 18 .
В ином случае она запрашивает подтверждение через confirm и возвращает его результат:
function checkAge(age) < if (age >18) < return true; >else < // . return confirm('Родители разрешили?'); >>
Будет ли эта функция работать как-то иначе, если убрать else ?
function checkAge(age) < if (age >18) < return true; >// . return confirm('Родители разрешили?'); >
Есть ли хоть одно отличие в поведении этого варианта?
Оба варианта функций работают одинаково, отличий нет.
Перепишите функцию, используя оператор '?' или '||'
важность: 4
Следующая функция возвращает true , если параметр age больше 18 .
В ином случае она задаёт вопрос confirm и возвращает его результат.
function checkAge(age) < if (age >18) < return true; >else < return confirm('Родители разрешили?'); >>
Перепишите функцию, чтобы она делала то же самое, но без if , в одну строку.
Сделайте два варианта функции checkAge :
- Используя оператор ?
- Используя оператор ||
function checkAge(age) < return (age >18) ? true : confirm('Родители разрешили?'); >
Используя оператор || (самый короткий вариант):
function checkAge(age) < return (age >18) || confirm('Родители разрешили?'); >
Обратите внимание, что круглые скобки вокруг age > 18 не обязательны. Они здесь для лучшей читаемости кода.