JS Функції
Оголошення функції
Починається з ключового слова function, після якого стоїть ім'я - дієслово, що відповідає на запитання «Що зробити?»
function multiply() { code }
Порядок передачі аргументів повинен відповідати порядку оголошених параметрів: значення першого аргументу буде присвоєно першому параметру, другого аргументу - другому параметру тощо. Якщо параметрів буде більше, ніж аргументів, то параметрам без значень буде присвоєно undefined
Оператор return без явно вказаного значення повертає спеціальне значення undefined. За відсутності return в тілі функції, вона все одно поверне undefined
Параметри за замовчуванням
function count(countFrom = 0, countTo = 10, step = 1)
Псевдомасив arguments
Псевдомасив - колекція з властивістю length і можливістю звернутися до елементу за індексом, але відсутністю більшості методів для роботи з масивом
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
Перетворення псевдомасиву (args[])
Зазвичай псевдомасив необхідно перетворити у повноцінний масив Використовуючи метод Array.from()
function fn() { // Змінна args буде містити повноцінний масив const args = Array.from(arguments); }
Використовуючи операцію ... (rest), вона дозволяє зібрати будь-яку кількість елементів, у нашому випадку аргументів, в масив, і зберегти його в змінну.
function fn(...args) { // Змінна args буде містити повноцінний масив }
Функціональний вираз
Функціональний вираз (function expression) - звичайне оголошення змінної, значенням якої буде функція. Альтернативний спосіб оголошення функції.
const multiply = function (x, y, z) { console.log(`Результат множення дорівнює ${x * y * z}`); };
Різниця в тому, що функціональний вираз не можна викликати до місця його створення, A оголошення функції можна викликати до місця її створення в коді.
Область видимості (пусто)
Стек викликів (пусто)
Переповнення стека викликів (пусто)
Колбек-функції
Функції не відрізняються від чисел, рядків або масивів - це просто спеціальний тип даних (об'єкт вищого порядку), значення, яке можна зберігати у змінній або передавати у якості аргументу в іншу функцію. Функція зворотного виклику (callback, колбек) - це функція, яка передається іншій функції як аргумент, а та, в свою чергу, викликає передану функцію.
// Колбек-функція function greet(name) { console.log(`Ласкаво просимо ${name}.`); } // Функція вищого порядку function registerGuest(name, callback) { console.log(`Реєструємо гостя ${name}.`); callback(name); } registerGuest("Манго", greet);
Інлайн колбеки
Якщо колбек-функція - маленька, і потрібна тільки для передачі аргументом, її можна оголосити безпосередньо на момент виклику функції, в яку передаємо колбек. Така функція буде доступна тільки у якості значення параметра і більше ніде в коді.
function registerGuest(name, callback) { console.log(`Реєструємо гостя ${name}.`); callback(name); } // Передаємо інлайн функцію greet у якості колбека registerGuest("Манго", function greet(name) { console.log(`Ласкаво просимо ${name}.`); }); // Передаємо інлайн функцію notify у якості колбека registerGuest("Полі", function notify(name) { console.log(`Шановний(а) ${name}, ваш номер буде готовий за 30 хвилин.`); });
Декілька колбеків
Функція може приймати будь-яку кількість колбеків. Наприклад, уявімо, що ми пишемо логіку прийняття дзвінків для телефону. Програма повинна увімкнути автовідповідач, якщо абонент - недоступний, або з'єднати дзвінок в іншому випадку. Доступність абонента будемо імітувати генератором випадкового числа, щоб між різними викликами функції можна було отримати різні результати.
function processCall(recipient) { // Імітуємо доступність абонента випадковим числом const isRecipientAvailable = Math.random() > 0.5; if (!isRecipientAvailable) { console.log(`Абонент ${recipient} недоступний, залиште повідомлення.`); // Логіка активації автовідповідача } else { console.log(`З'єднуємо з ${recipient}, очікуйте...`); // Логіка прийняття дзвінка } } processCall("Манго");
Ми могли б написати функцію таким чином, щоб вона повертала якесь значення, і потім за результатом її виконання, робити перевірки і виконувати потрібний код. Але перевірки не стосуються зовнішнього коду і будуть його засмічувати. Виконаємо рефакторинг функції таким чином, щоб вона приймала два колбеки onAvailable і onNotAvailable, і викликала їх за умовою.
function processCall(recipient, onAvailable, onNotAvailable) { // Імітуємо доступність абонента випадковим числом const isRecipientAvailable = Math.random() > 0.5; if (!isRecipientAvailable) { onNotAvailable(recipient); return; } onAvailable(recipient); } function takeCall(name) { console.log(`З'єднуємо з ${name}, очікуйте...`); // Логіка прийняття дзвінка } function activateAnsweringMachine(name) { console.log(`Абонент ${name} недоступний, залиште повідомлення.`); // Логіка активації автовідповідача } function leaveHoloMessage(name) { console.log(`Абонент ${name} недоступний, записуємо голограму.`); // Логіка запису голограми } processCall("Манго", takeCall, activateAnsweringMachine); processCall("Полі", takeCall, leaveHoloMessage);
Колбеки застосовуються для обробки дій користувача на сторінці, на момент обробки запитів на сервер, виконання заздалегідь невідомих функцій тощо. У цьому і полягає їх суть - це функції, призначені для відкладеного виконання.
Абстракція - приховування деталей реалізації. Дозволяє думати про задачі на вищому (абстрактному) рівні. Функції - це хороший спосіб побудови абстракцій.
function printValue(value) { console.log(value); } function prettyPrint(value) { console.log("Logging value: ", value); } function repeat(n, action) { for (let i = 0; i fgh n; i += 1) { action(i); } } // Передаємо printValue як callback-функцію repeat(3, printValue); // 0 // 1 // 2 // Передаємо prettyPrint як callback-функцію repeat(3, prettyPrint); // Logging value: 0 // Logging value: 1 // Logging value: 2
Стрілочні функції
Стрілочні функції мають скорочений, лаконічніший синтаксис, що зменшує обсяг коду, особливо коли функція маленька або якщо вона використовується як колбек.
Усі стрілки створюються як функціональний вираз, і якщо функція - не анонімна, її необхідно присвоювати змінній.
У них відсутній arguments
const foo = (...rest) => { console.log(rest); // [1,5,7,9,4,5] } foo(1,5,7,9,4,5);
// Звичайне оголошення функції function classicAdd(a, b, c) { return a + b + c; } // Те саме стрілочною функцією const arrowAdd = (a, b, c) => { return a + b + c; };
Якщо параметр один, його можна оголошувати без круглих дужок.
const add = a => { return a + 5; };
Якщо параметри відсутні, то обов'язково повинні бути порожні круглі дужки.
const greet = () => { console.log("Привіт!"); }; супер короткий запис () => {};
Існує два варіанти: з фігурними дужками і без них. Якщо є фігурні дужки, і функція повинна повертати якесь значення, необхідно явно поставити return. Це називається явне повернення (explicit return). Такий синтаксис використовується у разі, якщо в тілі функції потрібно виконати ще якісь інструкції, крім повернення значення.
Якщо фігурні дужки відсутні, то повертається результат виразу, який стоїть після =/. Це називається неявне повернення
const add = (a, b, c) => a + b + c;
доречний тільки тоді, коли в тілі функції не потрібно виконувати жодних додаткових інструкцій, крім повернення значення.
У стрілочних функцій немає локальної змінної arguments.Якщо необхідно зібрати всі аргументи в масив, використовується операція rest
const add = (...args) => { console.log(args); }; add(1, 2, 3); // [1, 2, 3]
Анонімні стрілочні функції відмінно підходять як колбеки для перебираючих методів масиву завдяки коротшому синтаксису оголошення
const numbers = [5, 10, 15, 20, 25]; // Оголошення функції numbers.forEach(function (number, index) { console.log(`Індекс ${index}, значення ${number}`); }); // Анонімна стрілочна функція numbers.forEach((number, index) => { console.log(`Індекс ${index}, значення ${number}`); });
Стрілочну колбек-функцію також можна оголошувати окремо і передавати на неї посилання. Це варто робити, якщо одна функція використовується у декількох місцях програми або якщо вона громіздка.
const logMessage = (number, index) => { console.log(`Індекс ${index}, значення ${number}`); }; numbers.forEach(logMessage);
Чисті функції
Функція з побічними ефектами - це функція, яка в процесі виконання може змінювати або використовувати глобальні змінні, змінювати значення аргументів посилального типу, виконувати операції введення-виведення тощо.
Чиста функція (pure function) - це функція, результат якої залежить тільки від значень переданих аргументів. За умови однакових аргументів вона завжди повертає один і той самий результат і не має побічних ефектів, тобто не змінює значення аргументів
Напишемо реалізацію чистої функції множення елементів масиву, що повертає новий масив, не змінюючи вихідний.
const pureMultiply = (array, value) => { const newArray = []; array.forEach(element => { newArray.push(element * value); }); return newArray; }; const numbers = [1, 2, 3, 4, 5]; const doubledNumbers = pureMultiply(numbers, 2); // Мутація вихідних даних не відбулася console.log(numbers); // [1, 2, 3, 4, 5] // Функція повернула новий масив зі зміненими даними console.log(doubledNumbers); // [2, 4, 6, 8, 10]
this Правила визначення
У глобальній області видимості, якщо скрипт виконується не в суворому режимі, this посилається на об'єкт window. В суворому режимі значення this, в глобальній області видимості, буде undefined.
function showThis() { console.log("this in showThis: ", this); } // Викликаємо у глобальному контексті showThis(); // this in showThis: Window const user = { username: "Mango", }; // Записуємо посилання на функцію у властивість об'єкта // Зверніть увагу, що це не виклик - немає () user.showContext = showThis; // Викликаємо функцію в контексті об'єкта // this буде вказувати на поточний об'єкт, в контексті // якого здійснюється виклик, а не на глобальний об'єкт. user.showContext(); // this in showThis: {username: "Mango", showContext: ƒ}
const customer = { firstName: "Jacob", lastName: "Mercer", getFullName() { return `${this.firstName} ${this.lastName}`; }, }; function makeMessage(callback) { // callback() - це виклик методу getFullName без об'єкта console.log(`Обробляємо заявку від ${callback()}.`); } makeMessage(customer.getFullName); // Буде помилка у виклику функції
this у стрілочних функціях
const showThis = () => { console.log("this in showThis: ", this); }; showThis(); // this in showThis: window const user = { username: "Mango", }; user.showContext = showThis; user.showContext(); // this in showThis: window
Методи функцій call, apply і bind
Трапляються ситуації, коли функцію потрібно викликати в контексті об'єкта, при цьому функція не є його методом. Для цього у функцій є методи call, apply і bind.
Метод call()
foo.call(obj, arg1, arg2, ...)
Метод call викличе функцію foo таким чином, що в this буде посилання на об'єкт obj, а також передасть аргументи arg1, arg2 тощо.
function greetGuest(greeting) { console.log(`${greeting}, ${this.username}.`); } const mango = { username: "Манго", }; const poly = { username: "Полі", }; greetGuest.call(mango, "Ласкаво просимо"); // Ласкаво просимо, Манго. greetGuest.call(poly, "З прибуттям"); // З прибуттям, Полі
Метод apply
foo.apply(obj, [arg1, arg2, ...])
Метод apply - це аналог методу call за винятком того, що синтаксис передачі аргументів вимагає не перерахування, а масив, навіть якщо аргумент всього один.
Метод apply викличе функцію foo таким чином, що в this буде посилання на об'єкт obj, а також передасть елементи масиву як окремі аргументи arg1, arg2 тощо.
function greetGuest(greeting) { console.log(`${greeting}, ${this.username}.`); } const mango = { username: "Манго", }; const poly = { username: "Полі", }; greetGuest.apply(mango, ["Ласкаво просимо"]); // Ласкаво просимо, Манго. greetGuest.apply(poly, ["З прибуттям"]); // З прибуттям, Полі.
Метод bind()
foo.bind(obj, arg1, arg2, ...)
Методи call і apply викликають функцію «на місці», тобто відразу. Але у разі колбек-функцій, коли необхідно не відразу викликати функцію, а передати посилання на неї, причому з прив'язаним контекстом, використовується метод bind.
Метод bind створює і повертає копію функції foo з прив'язаним контекстом obj і аргументами arg1, arg2 тощо. Утворюється копія функції, яку можна передати куди завгодно і викликати коли завгодно.
function greet(clientName) {
return `${clientName}, ласкаво просимо в «${this.service}».`;
}
const steam = {
service: "Steam",
};
const steamGreeter = greet.bind(steam);
steamGreeter("Манго"); // "Манго, ласкаво просимо в «Steam»."
const gmail = {
service: "Gmail",
};
const gmailGreeter = greet.bind(gmail);
gmailGreeter("Полі"); // "Полі, ласкаво просимо в «Gmail»."
bind() і методи об'єкта
У разі передачі методів об'єкта як колбек-функцій, контекст не зберігається. Колбек - це посилання на метод, яка присвоюється як значення параметра, що викликається без об'єкта.
const customer = { firstName: "Jacob", lastName: "Mercer", getFullName() { return `${this.firstName} ${this.lastName}`; }, }; function makeMessage(callback) { // callback() - це виклик методу getFullName без об'єкта console.log(`Обробляємо заявку від ${callback()}.`); } makeMessage(customer.getFullName); // Виникне помилка на момент виклику функції
У суворому режимі, значення this в методі getFullName, викликаючи як колбек-функції callback(), буде undefined. Звертаючись до властивостей firstName і lastName, виникне помилка, оскільки undefined - це не об'єкт. Метод bind використовується для прив'язування контексту, передаючи методи об'єкта як колбек-функції. Передамо колбеком не оригінальний метод getFullName, а його копію з прив'язаним контекстом об'єкту customer.
// ❌ Було makeMessage(customer.getFullName); // Виникне помилка на момент виклику функції // ✅ Стало makeMessage(customer.getFullName.bind(customer)); // Обробляємо заявку від Jacob Mercer.