Перейти до основного вмісту

Функції

Функція - це підпрограма, незалежна частина коду, призначена для багаторазового виконання конкретної задачі з різними початковими значеннями. Функції дозволяють структурувати великі програми, зменшують повторення та ізолюють код.

Функцію можна уявити у вигляді чорного ящика: вона отримує щось на вході (дані), і повертає щось на виході (результат виконання коду всередині неї).

function as a box

Оголошення функції

// 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);
Цікаво

Не важливо, який синтаксис використовувати, важливо, щоб код в проекті був однорідним. Тобто необхідно намагатися не змішувати оголошення функції з функціональними виразами.