Функції
Функція - це підпрограма, незалежна частина коду, призначена для багаторазового виконання конкретної задачі з різними початковими значеннями. Функції дозволяють структурувати великі програми, зменшують повторення та ізолюють код.
Функцію можна уявити у вигляді чорного ящика: вона отримує щось на вході (дані), і повертає щось на виході (результат виконання коду всередині неї).
Оголошення функції
// 1. Оголошення функції multiply
function multiply() {
// Тіло функції
console.log("Це лог на момент виклику функції multiply");
}
// 2. Виклики функції multiply
multiply(); // 'Це лог на момент виклику функції multiply'
multiply(); // 'Це лог на момент виклику функції multiply'
multiply(); // 'Це лог на момент виклику функції multiply'
Оголошення функції (function declaration) починається з ключового слова
function
, після якого стоїть ім'я - дієслово, що відповідає на запитання «Що
зробити?» і пара круглих дужок.
Тіло функції береться у фігурні дужки {}
і містить інструкції, які необхідно
виконати на момент її виклику. Потім, коли необхідно, функція
викликається за допомогою імені і пари круглих дужок.
Параметри та аргументи
В круглих дужках після імені функції зазначаються параметри - перелік даних, які функція очікує на момент виклику.
// Оголошення параметрів x, y, z
function multiply(x, y, z) {
console.log(`Результат множення дорівнює ${x * y * z}`);
}
Параметри - це локальні змінні, доступні тільки у тілі функції. Вони розділяються комами. Параметрів може бути декілька, або взагалі не бути, у такому випадку записуються просто порожні круглі дужки.
Параметри будуть створюватися кожного разу під час виконання функції, і їх окремі інкарнації жодним чином один з одним не пов'язані.
На момент виклику функції, в круглих дужках можна передати аргументи - значення для оголошених параметрів функції.
// 1. Оголошення параметрів x, y, z
function multiply(x, y, z) {
console.log(`Результат множення дорівнює ${x * y * z}`);
}
// 2. Передача аргументів
multiply(2, 3, 5); // Результат множення дорівнює 30
multiply(4, 8, 12); // Результат множення дорівнює 384
multiply(17, 6, 25); // Результат множення дорівнює 2550
Порядок передачі аргументів повинен відповідати порядку оголошених параметрів:
значення першого аргументу буде присвоєно першому параметру, другого аргументу -
другому параметру тощо. Якщо параметрів буде більше, ніж аргументів, то
параметрам без значень буде присвоєно undefined
.
Повернення значення
Оператор return
використовується для передачі значення з тіла функції у
зовнішній код. Коли інтерпретатор зустрічає return
, він відразу ж виходить з
функції (припиняє її виконання), і повертає вказане значення у те місце коду, де
була викликана функція.
function multiply(x, y, z) {
console.log("Код до return виконується звичайним чином");
// Повертаємо результат виразу множення
return x * y * z;
console.log("Цей лог ніколи не виконається, він стоїть після return");
}
// Результат роботи функції можна зберегти у змінну
let result = multiply(2, 3, 5);
console.log(result); // 30
result = multiply(4, 8, 12);
console.log(result); // 384
result = multiply(17, 6, 25);
console.log(result); // 2550
Оператор return
без явно вказаного значення повертає спеціальне значення
undefined
. За відсутності return
в тілі функції, вона все одно поверне
undefined
.
Порядок виконання коду
Коли інтерпретатор зустрічає виклик функції (або методу), він призупиняє виконання поточного коду і починає виконувати код з тіла функції. Після того як увесь код функції буде виконаний, інтерпретатор виходить з тіла функції, повертаючись у те місце, звідки прийшов, і продовжує виконувати код, наступний після виклику функції.
function multiply(x, y, z) {
console.log(`Результат множення дорівнює ${x * y * z}`);
}
console.log("Лог до виклику функції multiply");
multiply(2, 3, 5); // Результат множення дорівнює 30
console.log("Лог після виклику функції multiply");
// Послідовність логів в консолі
// "Лог до виклику функції multiply"
// "Результат множення дорівнює 30"
// "Лог після виклику функції multiply"
Параметри за замовчуванням
Іноді необхідно оголосити функцію, у параметрів якої будуть значення, відмінні
від undefined
, навіть якщо для них не передали аргументи. Це робиться дуже
простим та очевидним чином - достатньо вказати значення за замовчуванням
безпосередньо на момент оголошення параметрів у підписі функції. У випадку
такого запису, якщо для параметра не було передано значення аргументу,
використовується значення за замовчуванням.
function count(countFrom = 0, countTo = 10, step = 1) {
console.log(`countFrom = ${countFrom}, countTo = ${countTo}, step = ${step}`);
for (let i = countFrom; i <= countTo; i += step) {
console.log(i);
}
}
count(1, 5); // countFrom = 1, countTo = 5, step = 1
count(2); // countFrom = 2, countTo = 10, step = 1
count(); // countFrom = 0, countTo = 10, step = 1
Псевдомасив arguments
Доступ до списку всіх аргументів можна отримати за допомогою спеціальної
змінної arguments
, яка доступна тільки всередині функції і зберігає всі
аргументи у якості псевдомасиву.
Псевдомасив - колекція з властивістю length
і можливістю звернутися до
елементу за індексом, але відсутністю більшості методів для роботи з масивом.
Розглянемо приклад використання arguments
у функції, яка множить будь-яку
кількість аргументів:
function multiply() {
let total = 1;
for (const argument of arguments) {
total *= argument;
}
return total;
}
console.log(multiply(1, 2, 3)); // 6
console.log(multiply(1, 2, 3, 4)); // 24
console.log(multiply(1, 2, 3, 4, 5)); // 120
Перетворення псевдомасиву
Зазвичай псевдомасив необхідно перетворити у повноцінний масив, оскільки у
псевдомасиву відсутні методи масиву, наприклад slice()
або includes()
. На
практиці застосовують декілька основних способів.
Використовуючи метод Array.from()
, який створить масив із псевдомасиву.
function fn() {
// Змінна args буде містити повноцінний масив
const args = Array.from(arguments);
}
Використовуючи операцію ...
(rest), вона дозволяє зібрати будь-яку кількість
елементів, у нашому випадку аргументів, в масив, і зберегти його в змінну.
Збираємо всі аргументи, використовуючи операцію rest
безпосередньо в підписі
функції.
function fn(...args) {
// Змінна args буде містити повноцінний масив
}
Операція rest
детальніше розглядається далі на курсі, тут показаний один з її
можливих варіантів застосування.
Патерн «Раннє повернення»
Оператор if...else
- це основний спосіб створення розгалужень. Проте, складні
вкладені розгалуження роблять код заплутаним для розуміння.
Створимо функцію, яка обробляє зняття коштів з особистого рахунку в банку. Вона отримує суму для зняття і поточний баланс рахунку, після чого, залежно від умови, виконує необхідний блок коду.
function withdraw(amount, balance) {
if (amount === 0) {
console.log("Для проведення операції введіть суму більшу за нуль");
} else if (amount > balance) {
console.log("Недостатньо коштів на рахунку");
} else {
console.log("Операція зняття коштів проведена успішно");
}
}
withdraw(0, 300); // "Для проведення операції введіть суму більшу за нуль"
withdraw(500, 300); // "Недостатньо коштів на рахунку"
withdraw(100, 300); // "Операція зняття коштів проведена успішно"
Навіть у такому простому прикладі є група вкладених умовних операторів, серед яких не одразу можна зрозуміти логіку виконання коду.
У функції може бути більше одного оператора return
. Головне пам'ятати, що
виконання функції переривається, коли інтерпретатор зустрічає повернення, і
увесь код після нього буде проігнорований в поточному виконанні функції.
Патерн «Раннє повернення» - це спосіб використовувати можливість
дострокового повернення з функції за допомогою оператора return
.
Використовуючи цей прийом, ми отримуємо чистіший, плоскіший і зрозуміліший код,
який простіше рефакторити.
Виділимо всі перевірки умов в окремі оператори if
, після чого додамо код, що
знаходиться в тілі else
. В ідеальному випадку, повинен вийти плоский список
умовних операторів, що йдуть один за одним, а в кінці - блок, який виконається
тільки у тому випадку, якщо не виконається жоден if
.
function withdraw(amount, balance) {
// Якщо умова виконується, викликається console.log
// і вихід із функції. Код після тіла if не виконається.
if (amount === 0) {
console.log("Для проведення операції введіть суму більшу за нуль");
return;
}
// Якщо умова першого if не виконалась, його тіло пропускається
// та інтерпретатор доходе до другого if.
// Якщо умова виконується, викликається console.log і вихід із функції.
// Код, що знаходиться після тіла if, не виконається.
if (amount > balance) {
console.log("Недостатньо коштів на рахунку");
return;
}
// Якщо жоден із попередніх if не виконався,
// інтерпретатор доходить до цього коду і виконує його.
console.log("Операція зняття коштів проведена");
}
withdraw(0, 300); // "Для проведення операції введіть суму більшу за нуль"
withdraw(500, 300); // "Недостатньо коштів на рахунку"
withdraw(100, 300); // "Операція зняття коштів проведена"
Функціональний вираз
Функціональний вираз (function expression) - звичайне оголошення змінної, значенням якої буде функція. Альтернативний спосіб оголошення функції.
// Оголошення функції (function declaration)
function multiply(x, y, z) {
console.log(`Результат множення дорівнює ${x * y * z}`);
}
// Функціональний вираз (function expression)
const multiply = function (x, y, z) {
console.log(`Результат множення дорівнює ${x * y * z}`);
};
Різниця в тому, що функціональний вираз не можна викликати до місця його
створення, тільки після нього, тому що це буквально оголошення const
змінної.
// ❌ Помилка! Не працює виклик до оголошення
multiply(1, 2, 3);
const multiply = function (x, y, z) {
console.log(`Результат множення дорівнює ${x * y * z}`);
};
// ✅ Працює виклик після оголошення
multiply(4, 5, 6);
A оголошення функції можна викликати до місця її створення в коді.
// ✅ Працює виклик до оголошення
multiply(1, 2, 3);
function multiply(x, y, z) {
console.log(`Результат множення дорівнює ${x * y * z}`);
}
// ✅ Працює виклик після оголошення
multiply(4, 5, 6);
Не важливо, який синтаксис використовувати, важливо, щоб код в проекті був однорідним. Тобто необхідно намагатися не змішувати оголошення функції з функціональними виразами.