Функції
Загалом кажучи, функція – це "підпрограма", що може бути викликана кодом, зовнішнім (або внутрішнім – у випадку рекурсії) щодо самої функції. Подібно до самої програми, функція складається з послідовності інструкцій, що називається тілом функції. Значення можуть бути передані функції як параметри, і функція повертає значення.
У JavaScript функції є об'єктами першого класу, тому що їх можна передавати в інші функції, повертати з функцій, присвоювати змінним і властивостям. Також вони можуть мати властивості та методи, як і будь-який інший об'єкт. Від інших об'єктів їх відрізняє те, що функцію можна викликати.
Більше прикладів і пояснень дивіться в Посібнику JavaScript про функції.
Опис
Значення функцій зазвичай є примірниками Function
. Дивіться інформацію про властивості та методи об'єктів Function
на сторінці Function
. Викличні значення змушують typeof
повертати "function"
, а не "object"
.
[!NOTE] Не всі викличні значення є примірниками
Function
. Наприклад, об'єктFunction.prototype
є викличним, але не є примірникомFunction
. Також можна вручну задати ланцюжок прототипів функції так, щоб вона більше не успадковувала властивостіFunction.prototype
. Однак такі випадки дуже рідкісні.
Повернене значення
Усталено, якщо виконання функції не закінчується інструкцією return
, або якщо після ключового слова return
немає виразу, то поверненим значенням стає undefined
. Інструкція return
дає змогу повернути з функції довільне значення. Один виклик функції може повернути лише одне значення, але можна імітувати ефект повернення декількох значень, повернувши об'єкт або масив і деструктурувавши результат.
[!NOTE] Конструктори, викликані з
new
, використовують для визначення поверненого значення іншу логіку.
Передача аргументів
Параметри та аргументи мають дещо різні значення, але у ВебДоках ми нерідко використовуємо ці терміни як взаємозамінні. Як стислий приклад:
function formatNumber(num) {
return num.toFixed(2);
}
formatNumber(2);
У цьому прикладі змінна num
зветься параметром функції: вона оголошена оточеному дужками списку визначення функції. Функція очікує того, що параметр num
буде числом – хоча це не можна забезпечити в JavaScript без написання коду валідації при виконанні. У виклику formatNumber(2)
число 2
є аргументом функції: це значення, що фактично передається функції у виклику функції. Значення аргументу можна отримати всередині тіла функції через відповідне ім'я параметра або об'єкт arguments
.
Аргументи завжди передаються за значенням і ніколи не передаються за посиланням. Це означає, що якщо функція повторно присвоює щось параметру, то значення поза функцією не змінюється. Якщо точніше, то аргументи-об'єкти передаються за співвикористанням, тобто якщо змінюються властивості об'єкта, то такі зміни впливають на цей об'єкт поза функцією. Наприклад:
function updateBrand(obj) {
// Внесення змін до об'єкта спостерігається поза функцією
obj.brand = "Toyota";
// Спроба переприсвоїти параметр, але це не вплине
// на значення змінної поза функцією
obj = null;
}
const car = {
brand: "Honda",
model: "Accord",
year: 1998,
};
console.log(car.brand); // Honda
// Передача об'єктного посилання до функції
updateBrand(car);
// updateBrand вносить зміни до car
console.log(car.brand); // Toyota
Ключове слово this
посилається на об'єкт, на якому відбувається звертання до функції – воно не вказує на наразі виконувану функцію, тож необхідно звертатися до значення функції за назвою, навіть всередині її тіла.
Визначення функцій
Грубо кажучи, JavaScript має чотири різновиди функцій:
- Звичайна функція – може повернути що завгодно; бувши закликана, завжди виконується до кінця
- Генераторна функція – повертає об'єкт
Generator
; може бути призупинена та відновлена операторомyield
- Асинхронна функція – повертає
Promise
; може бути призупинена та відновлена операторомawait
- Асинхронна генераторна функція – повертає об'єкт
AsyncGenerator
; можна використовувати як операторawait
, так і операторyield
Для кожного різновиду функції є кілька способів визначення:
- Оголошення
- Вираз
- Конструктор
Function()
,GeneratorFunction()
,AsyncFunction()
,AsyncGeneratorFunction()
На додачу до цього, є особливий синтаксис для визначення стрілкових функцій та методів, які надають більш точну семантику для їх використання. Класи концептуально не є функціями (тому, що вони викидають помилку при виклику без new
), але також успадковують від Function.prototype
та мають typeof MyClass === "function"
.
// Конструктор
const multiply = new Function("x", "y", "return x * y");
// Оголошення
function multiply(x, y) {
return x * y;
} // Немає потреби ставити тут крапку з комою
// Вираз; ця функція є анонімною, але присвоюється змінній
const multiply = function (x, y) {
return x * y;
};
// Вираз; ця функція має власну назву
const multiply = function funcName(x, y) {
return x * y;
};
// Стрілкова функція
const multiply = (x, y) => x * y;
// Метод
const obj = {
multiply(x, y) {
return x * y;
},
};
Кожен з цих варіантів запису робить приблизно одне й те ж, але є деякі відмінності у логіці.
- Записи конструктора
Function()
, виразуfunction
і оголошенняfunction
створюють повноцінні об'єкти функцій, які можна конструювати за допомогоюnew
. Натомість стрілкові функції та методи не можна конструювати. Асинхронні функції, генераторні функції та асинхронні генераторні функції не можна конструювати, незалежно від запису. - Оголошення
function
створює функції, які піднімаються. Решта записів не піднімає функцію, і значення функції тоді видно лише після визначення. - Стрілкові функції та конструктор
Function()
завжди створюють анонімні функції, тобто їх не можна легко викликати рекурсивно. Один зі способів викликати стрілкову функцію рекурсивно – присвоїти її змінній. - Запис стрілкових функцій не має доступу до
arguments
іthis
. - Конструктор
Function()
не може звертатися до жодних локальних змінних – він має доступ лише до глобальної області видимості. - Конструктор
Function()
призводить до компіляції при виконанні та нерідко є повільнішим за решту записів.
Що до виразів function
, то є різниця між назвою функції та змінною, якій ця функція присвоєна. Назва функції не може бути змінена, а змінна, якій функція присвоєна, може бути присвоєна повторно. Назва функції може відрізнятися від змінної, якій функція присвоєна: вони не мають взаємозв'язку. Назва функції може вживатися лише всередині тіла функції. Спроби використати його поза тілом функції призведе до помилки (або до отримання іншого значення, якщо та сама назва оголошена ще десь). Наприклад:
const y = function x() {};
console.log(x); // ReferenceError: x is not defined
З іншого боку, змінна, якій присвоєна функція, обмежується лише своєю областю видимості, яка гарантовано включає область видимості, у якій функція оголошена.
Оголошення функції також створює змінну з такою ж назвою, як і назва функції. Таким чином, на відміну від функцій, визначених виразами функції, до функцій, визначених оголошеннями функції, можна звертатися за їхньою назвою в межах області видимості, в якій вони визначені, а також всередині їхнього власного тіла.
Функції, визначеній new Function
, вихідний код збирається динамічно, що можна помітити, якщо її серіалізувати. Наприклад, console.log(new Function().toString())
дає:
function anonymous(
) {
}
Це реальний вихідний код, що вживається для компіляції функції. Проте попри те, що конструктор Function()
створює функцію з назвою anonymous
, ця назва не додається до області видимості тіла. Тіло має доступ лише до глобальних змінних. Наприклад, наступний код призведе до помилки:
new Function("alert(anonymous);")();
Функція, визначена виразом функції або оголошенням функції, успадковує поточну область видимості. Тобто така функція утворює замикання. З іншого боку, функція, визначена конструктором Function
, не успадковує жодної області видимості, крім глобальної (яку успадковують усі функції).
// p – глобальна змінна
globalThis.p = 5;
function myFunc() {
// p – локальна змінна
const p = 9;
function decl() {
console.log(p);
}
const expr = function () {
console.log(p);
};
const cons = new Function("\tconsole.log(p);");
decl();
expr();
cons();
}
myFunc();
// Виводить:
// 9 (для 'decl' з оголошення функції (поточна область видимості))
// 9 (для 'expr' з виразу функції (поточна область видимості))
// 5 (для 'cons' з конструктора Function (глобальна область видимості))
Функції, визначені виразами функції та оголошеннями функції, розбираються лише раз, а функція, визначена конструктором Function
, розбирає рядок, переданий їй, кожного разу, коли конструктор викликається. Хоча вираз функції створює замикання кожного разу, тіло не розбирається заново, тож вирази функцій все ж швидші за new Function(...)
. Таким чином, конструктора Function
слід уникати, коли це можливо.
Оголошення функції може ненавмисно перетворитися на вираз функції, коли воно зустрічається в контексті виразу.
// Оголошення функції
function foo() {
console.log("АГОВ!");
}
doSomething(
// Вираз функції, переданий як аргумент
function foo() {
console.log("АГОВ!");
},
);
З іншого боку, вираз функції також може перетворитися на оголошення функції. Інструкція-вираз не може починатися з ключових слів function
або async function
, що є поширеною помилкою при реалізації IIFE (негайно закликаних виразів функцій).
function () { // SyntaxError: Function statements require a function name
console.log("АГОВ!");
}();
function foo() {
console.log("АГОВ!");
}(); // SyntaxError: Unexpected token ')'
Замість цього, слід розпочати інструкцію-вираз чимось іншим, щоб ключове слово function
однозначно відкривало вираз функції. Поширеними варіантами є групування та використання void
.
(function () {
console.log("АГОВ!");
})();
void function () {
console.log("АГОВ!");
}();
Параметри функції
Кожний параметр функції є простим ідентифікатором, до якого можна звернутися в локальній області видимості.
function myFunc(a, b, c) {
// Тут можна звертатися до значень a, b та c
}
Є три особливі синтаксиси параметрів:
- Усталені параметри дають формальним параметрам змогу ініціалізуватися усталеними значеннями, якщо не передано жодного значення або передано
undefined
. - Решта параметрів дає змогу представляти нескінченну кількість аргументів у вигляді масиву.
- Деструктурування дає змогу розпаковувати елементи з масивів або властивості з об'єктів у окремі змінні.
function myFunc({ a, b }, c = 1, ...rest) {
// Тут можна звертатися до значень a, b, c та rest
}
Є певні наслідки, якщо використовується один зі синтаксисів непростих параметрів, наведених вище:
- Не можна застосовувати до тіла функції
"use strict"
: це призводить до синтаксичної помилки. - Навіть якщо функція не перебуває в суворому режимі, застосовуються певні особливості функцій у суворому режимі, як-от об'єкт
arguments
перестає синхронізуватися з іменованими параметрами, а властивістьarguments.callee
викидає помилку, якщо спробувати до неї звернутися, а також заборонене дублювання імен параметрів.
Об'єкт arguments
До аргументів функції всередині неї можна звернутися за допомогою об'єкта arguments
.
arguments
Масивоподібний об'єкт, що вміщає аргументи, передані до функції, що наразі виконується.
arguments.callee
Функція, що наразі виконується.
arguments.length
Число аргументів, переданих до функції.
Функції-гетери та функції-сетери
На будь-якому стандартному вбудованому або визначеному користувачем об'єкті, що підтримує додавання нових властивостей, можна визначити аксесорні властивості. У літералах об'єктів та класах можна використовувати спеціальні синтаксиси для визначення гетера та сетера аксесорної властивості.
- get
Зв'язує властивість об'єкта з функцією, що буде викликана, коли до цієї властивості відбудеться звертання.
- set
Зв'язує властивість об'єкта з функцією, що буде викликана, коли відбудеться спроба присвоєння цієї властивості.
Зверніть увагу, що такий синтаксис створює властивість об'єкта, а не метод. Функцію-гетер або функцію-сетер можна отримати лише за допомогою API рефлексії, наприклад, Object.getOwnPropertyDescriptor()
.
Функції блокового рівня
У суворому режимі функція всередині блоку обмежена областю видимості цього блоку. До ES2015 функції блокового рівня були заборонені в суворому режимі.
"use strict";
function f() {
return 1;
}
{
function f() {
return 2;
}
}
f() === 1; // true
// f() === 2 у несуворому режимі
Функції блокового рівня в несуворому коді
Якщо коротко: Не робіть цього.
У несуворому коді оголошення функцій всередині блоків поводяться дивно. Наприклад:
if (shouldDefineZero) {
function zero() {
// НЕБЕЗПЕКА: ризик проблем сумісності
console.log("Це нуль.");
}
}
Семантика цього в суворому режимі описана як слід: zero
існує лише в межах області видимості блоку if
. Якщо shouldDefineZero
– хиба, то функція zero
повинна ніколи не бути визначена, оскільки цей блок ніколи не виконується. Проте історично це було залишено невизначеним, тож різні браузери реалізували це в несуворому режимі по-різному. Більше про це – дивіться довідку оголошення function
.
Безпечніший спосіб умовно визначати функції – присвоювати вирази функції змінним:
// Завдяки використанню var ця змінна доступна як глобальна,
// що наближає логіку до оголошення функції вищого рівня
var zero;
if (shouldDefineZero) {
zero = function () {
console.log("Це нуль.");
};
}
Приклади
Повернення відформатованого числа
Наступна функція повертає рядок, що містить відформатоване представлення числа, вирівняне зліва нулями.
// Ця функція повертає рядок, вирівняний зліва нулями
function padZeros(num, totalLen) {
let numStr = num.toString(); // Ініціалізувати повернене значення як рядок
const numZeros = totalLen - numStr.length; // Обчислити число нулів
for (let i = 1; i <= numZeros; i++) {
numStr = `0${numStr}`;
}
return numStr;
}
Наступні інструкції викликають функцію padZeros
.
let result;
result = padZeros(42, 4); // повертає "0042"
result = padZeros(42, 2); // повертає "42"
result = padZeros(5, 4); // повертає "0005"
З'ясування, чи існує функція
Чи існує функція, можна з'ясувати за допомогою оператора typeof
. У наступному прикладі виконується перевірка, щоб з'ясувати, чи об'єкт window
має властивість noFunc
, яка є функцією. Якщо так, вона використовується; інакше виконується якась інша дія.
if (typeof window.noFunc === "function") {
// використати noFunc()
} else {
// якась інша дія
}
Зверніть увагу, що в перевірці if
використовується звертання до noFunc
: після назви функції немає дужок ()
, тож сама функція не викликається.
Специфікації
Сумісність із браузерами
desktop | mobile | server | ||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
functions
|
Chrome Full support 1 | Edge Full support 12 | Firefox Full support 1 | Internet Explorer Full support 3 | Opera Full support 3 | Safari Full support 1 | WebView Android Full support 1 | Chrome Android Full support 18 | Firefox for Android Full support 4 | Opera Android Full support 10.1 | Safari on iOS Full support 1 | Samsung Internet Full support 1.0 | Deno Full support 1.0 | Node.js Full support 0.10.0 |
arguments
|
Chrome Full support 1 | Edge Full support 12 | Firefox Full support 1 | Internet Explorer Full support 3 | Opera Full support 3 | Safari Full support 1 | WebView Android Full support 1 | Chrome Android Full support 18 | Firefox for Android Full support 4 | Opera Android Full support 10.1 | Safari on iOS Full support 1 | Samsung Internet Full support 1.0 | Deno Full support 1.0 | Node.js Full support 0.10.0 |
arguments.@@iterator
|
Chrome Full support 52 | Edge Full support 12 | Firefox Full support 46 | Internet Explorer No support Ні | Opera Full support 39 | Safari Full support 9 | WebView Android Full support 52 | Chrome Android Full support 52 | Firefox for Android Full support 46 | Opera Android Full support 41 | Safari on iOS Full support 9 | Samsung Internet Full support 6.0 | Deno Full support 1.0 | Node.js Full support 4.0.0 |
arguments.callee
|
Chrome Full support 1 | Edge Full support 12 | Firefox Full support 1 | Internet Explorer Full support 6 | Opera Full support 4 | Safari Full support 1 | WebView Android Full support 1 | Chrome Android Full support 18 | Firefox for Android Full support 4 | Opera Android Full support 10.1 | Safari on iOS Full support 1 | Samsung Internet Full support 1.0 | Deno Full support 1.0 | Node.js Full support 0.10.0 |
arguments.length
|
Chrome Full support 1 | Edge Full support 12 | Firefox Full support 1 | Internet Explorer Full support 4 | Opera Full support 4 | Safari Full support 1 | WebView Android Full support 1 | Chrome Android Full support 18 | Firefox for Android Full support 4 | Opera Android Full support 10.1 | Safari on iOS Full support 1 | Samsung Internet Full support 1.0 | Deno Full support 1.0 | Node.js Full support 0.10.0 |
Arrow functions | Chrome Full support 45 | Edge Full support 12 | Firefox Full support 22 | Internet Explorer No support Ні | Opera Full support 32 | Safari Full support 10 | WebView Android Full support 45 | Chrome Android Full support 45 | Firefox for Android Full support 22 | Opera Android Full support 32 | Safari on iOS Full support 10 | Samsung Internet Full support 5.0 | Deno Full support 1.0 | Node.js Full support 4.0.0 |
Trailing comma in parameters
|
Chrome Full support 58 | Edge Full support 12 | Firefox Full support 52 | Internet Explorer No support Ні | Opera Full support 45 | Safari Full support 10 | WebView Android Full support 58 | Chrome Android Full support 58 | Firefox for Android Full support 52 | Opera Android Full support 43 | Safari on iOS Full support 10 | Samsung Internet Full support 7.0 | Deno Full support 1.0 | Node.js Full support 8.0.0 |
Block-level functions
|
Chrome Full support 49 | Edge Full support 12 | Firefox Full support 46 | Internet Explorer Full support 11 | Opera Full support 36 | Safari Full support 10 | WebView Android Full support 49 | Chrome Android Full support 49 | Firefox for Android Full support 46 | Opera Android Full support 36 | Safari on iOS Full support 10 | Samsung Internet Full support 5.0 | Deno Full support 1.0 | Node.js Full support 4.0.0 |
Default parameters | Chrome Full support 49 | Edge Full support 14 | Firefox Full support 15 | Internet Explorer No support Ні | Opera Full support 36 | Safari Full support 10 | WebView Android Full support 49 | Chrome Android Full support 49 | Firefox for Android Full support 15 | Opera Android Full support 36 | Safari on iOS Full support 10 | Samsung Internet Full support 5.0 | Deno Full support 1.0 | Node.js Full support 6.0.0 |
Destructured parameter with default value assignment
|
Chrome Full support 49 | Edge Full support 14 | Firefox Full support 41 | Internet Explorer No support Ні | Opera Full support 36 | Safari Full support 10 | WebView Android Full support 49 | Chrome Android Full support 49 | Firefox for Android Full support 41 | Opera Android Full support 36 | Safari on iOS Full support 10 | Samsung Internet Full support 5.0 | Deno Full support 1.0 | Node.js Full support 6.0.0 |
Parameters without defaults after default parameters
|
Chrome Full support 49 | Edge Full support 14 | Firefox Full support 26 | Internet Explorer No support Ні | Opera Full support 36 | Safari Full support 10 | WebView Android Full support 49 | Chrome Android Full support 49 | Firefox for Android Full support 26 | Opera Android Full support 36 | Safari on iOS Full support 10 | Samsung Internet Full support 5.0 | Deno Full support 1.0 | Node.js Full support 6.0.0 |
get
|
Chrome Full support 1 | Edge Full support 12 | Firefox Full support 1.5 | Internet Explorer Full support 9 | Opera Full support 9.5 | Safari Full support 3 | WebView Android Full support 1 | Chrome Android Full support 18 | Firefox for Android Full support 4 | Opera Android Full support 14 | Safari on iOS Full support 1 | Samsung Internet Full support 1.0 | Deno Full support 1.0 | Node.js Full support 0.10.0 |
Computed property names
|
Chrome Full support 46 | Edge Full support 12 | Firefox Full support 34 | Internet Explorer No support Ні | Opera Full support 47 | Safari Full support 9.1 | WebView Android Full support 46 | Chrome Android Full support 46 | Firefox for Android Full support 34 | Opera Android Full support 33 | Safari on iOS Full support 9.3 | Samsung Internet Full support 5.0 | Deno Full support 1.0 | Node.js Full support 4.0.0 |
Method definitions | Chrome Full support 39 | Edge Full support 12 | Firefox Full support 34 | Internet Explorer No support Ні | Opera Full support 26 | Safari Full support 9 | WebView Android Full support 39 | Chrome Android Full support 39 | Firefox for Android Full support 34 | Opera Android Full support 26 | Safari on iOS Full support 9 | Samsung Internet Full support 4.0 | Deno Full support 1.0 | Node.js Full support 4.0.0 |
Async generator methods
|
Chrome Full support 63 | Edge Full support 79 | Firefox Full support 55 | Internet Explorer No support Ні | Opera Full support 50 | Safari Full support 12 | WebView Android Full support 63 | Chrome Android Full support 63 | Firefox for Android Full support 55 | Opera Android Full support 46 | Safari on iOS Full support 12 | Samsung Internet Full support 8.0 | Deno Full support 1.0 | Node.js Full support 10.0.0 |
Async methods
|
Chrome Full support 55 | Edge Full support 15 | Firefox Full support 52 | Internet Explorer No support Ні | Opera Full support 42 | Safari Full support 10.1 | WebView Android Full support 55 | Chrome Android Full support 55 | Firefox for Android Full support 52 | Opera Android Full support 42 | Safari on iOS Full support 10.3 | Samsung Internet Full support 6.0 | Deno Full support 1.0 | Node.js Full support 7.6.0 |
Generator methods are not constructable (ES2016)
|
Chrome Full support 42 | Edge Full support 13 | Firefox Full support 43 | Internet Explorer No support Ні | Opera Full support 29 | Safari Full support 9.1 | WebView Android Full support 42 | Chrome Android Full support 42 | Firefox for Android Full support 43 | Opera Android Full support 29 | Safari on iOS Full support 9.3 | Samsung Internet Full support 4.0 | Deno Full support 1.0 | Node.js Full support 6.0.0 |
Rest parameters | Chrome Full support 47 | Edge Full support 12 | Firefox Full support 15 | Internet Explorer No support Ні | Opera Full support 34 | Safari Full support 10 | WebView Android Full support 47 | Chrome Android Full support 47 | Firefox for Android Full support 15 | Opera Android Full support 34 | Safari on iOS Full support 10 | Samsung Internet Full support 5.0 | Deno Full support 1.0 | Node.js Full support 6.0.0 |
Destructuring rest parameters
|
Chrome Full support 49 | Edge Full support 79 | Firefox Full support 52 | Internet Explorer No support Ні | Opera Full support 36 | Safari Full support 10 | WebView Android Full support 49 | Chrome Android Full support 49 | Firefox for Android Full support 52 | Opera Android Full support 36 | Safari on iOS Full support 10 | Samsung Internet Full support 5.0 | Deno Full support 1.0 | Node.js Full support 6.0.0 |
set
|
Chrome Full support 1 | Edge Full support 12 | Firefox Full support 1.5 | Internet Explorer Full support 9 | Opera Full support 9.5 | Safari Full support 3 | WebView Android Full support 1 | Chrome Android Full support 18 | Firefox for Android Full support 4 | Opera Android Full support 14 | Safari on iOS Full support 1 | Samsung Internet Full support 1.0 | Deno Full support 1.0 | Node.js Full support 0.10.0 |
Computed property names
|
Chrome Full support 46 | Edge Full support 12 | Firefox Full support 34 | Internet Explorer No support Ні | Opera Full support 47 | Safari Full support 9.1 | WebView Android Full support 46 | Chrome Android Full support 46 | Firefox for Android Full support 34 | Opera Android Full support 33 | Safari on iOS Full support 9.3 | Samsung Internet Full support 5.0 | Deno Full support 1.0 | Node.js Full support 4.0.0 |
Дивіться також
- Посібник Функції
- Класи
function
- Вираз
function
Function