Пошук уроків, статей та іншого контенту
Порівняєте call та apply і навчитеся явно викликати функцію з потрібним this і набором аргументів.
У JavaScript значення this залежить не від місця оголошення функції, а від способу її виклику.
Методи call та apply дають змогу явно вказати:
який об’єкт має бути значенням this;
які аргументи потрібно передати функції.
Це корисно, коли потрібно:
використати метод одного об’єкта для іншого;
викликати функцію з динамічним контекстом;
передати аргументи, які зберігаються в масиві;
повторно використати спільну логіку для різних об’єктів.
callСинтаксис:
function.call(thisArg, arg1, arg2, ...);call:
викликає функцію негайно;
встановлює передане значення як this;
передає аргументи окремими значеннями.
Розглянемо приклад:
function introduce(greeting, punctuation) {
return `${greeting}, мене звати ${this.name}${punctuation}`;
}
const user = {
name: "Олена"
};
const result = introduce.call(user, "Привіт", "!");
console.log(result);
// Привіт, мене звати Олена!Функція introduce не є методом об’єкта user. Проте за допомогою call ми тимчасово викликаємо її так, ніби вона належить цьому об’єкту.
Перший аргумент call — це значення this. Усі наступні аргументи передаються функції у звичайному порядку.
function multiply(a, b) {
return this.factor * a * b;
}
const settings = {
factor: 10
};
const result = multiply.call(settings, 2, 3);
console.log(result);
// 60Під час виклику:
multiply.call(settings, 2, 3);відбувається приблизно таке:
this всередині multiply посилається на settings;
a отримує значення 2;
b отримує значення 3.
applyСинтаксис:
function.apply(thisArg, [arg1, arg2, ...]);apply працює майже так само, як call, але передає всі аргументи одним масивом.
function introduce(greeting, punctuation) {
return `${greeting}, мене звати ${this.name}${punctuation}`;
}
const user = {
name: "Андрій"
};
const argumentsList = ["Вітаю", "."];
const result = introduce.apply(user, argumentsList);
console.log(result);
// Вітаю, мене звати Андрій.Перший аргумент — значення this, а другий — масив аргументів.
call та applyОбидва методи:
викликають функцію негайно;
дають змогу явно встановити this;
повертають результат виконання функції;
не змінюють саму функцію.
Головна відмінність — спосіб передавання аргументів:
function showValues(first, second, third) {
return [this.prefix, first, second, third];
}
const context = {
prefix: "Значення"
};
const fromCall = showValues.call(context, 1, 2, 3);
const fromApply = showValues.apply(context, [1, 2, 3]);
console.log(fromCall);
// ["Значення", 1, 2, 3]
console.log(fromApply);
// ["Значення", 1, 2, 3]Використовуйте:
call, коли аргументи вже є окремими значеннями;
apply, коли аргументи зберігаються в масиві або масивоподібній структурі.
У сучасному JavaScript часто можна замінити apply оператором розпилення:
const values = [1, 2, 3];
const result = showValues.call(context, ...values);
console.log(result);
// ["Значення", 1, 2, 3]За результатом цей код еквівалентний:
const result = showValues.apply(context, values);Метод можна позичити в іншого об’єкта за допомогою call.
const firstUser = {
name: "Марія",
sayHello() {
return `Привіт, я ${this.name}`;
}
};
const secondUser = {
name: "Петро"
};
const message = firstUser.sayHello.call(secondUser);
console.log(message);
// Привіт, я ПетроМетод sayHello оголошений усередині firstUser, але під час конкретного виклику this посилається на secondUser.
Цей підхід часто називають запозиченням методу.
apply зручно використовувати, коли кількість аргументів визначається під час виконання програми.
function calculateTotal(discount, tax, ...prices) {
const subtotal = prices.reduce((sum, price) => sum + price, 0);
const discountedTotal = subtotal * (1 - discount);
return discountedTotal * (1 + tax);
}
const cart = {
currency: "UAH"
};
const values = [0.1, 0.2, 100, 200, 50];
const total = calculateTotal.apply(cart, values);
console.log(`${total.toFixed(2)} ${cart.currency}`);
// 283.50 UAHУ цьому прикладі:
this всередині функції посилається на cart;
discount отримує 0.1;
tax отримує 0.2;
решта значень потрапляє в prices.
У цій конкретній функції this не використовується для обчислень, але він все одно може бути потрібен для форматування або доступу до властивостей контексту.
call і apply повертають результат функціїЦі методи не створюють нову функцію. Вони одразу запускають наявну функцію та повертають її результат.
function getFullName(firstName, lastName) {
return `${firstName} ${lastName} — ${this.role}`;
}
const employee = {
role: "розробник"
};
const fullName = getFullName.call(employee, "Ірина", "Коваль");
console.log(fullName);
// Ірина Коваль — розробникЯкщо функція нічого явно не повертає, результатом буде undefined.
function printName() {
console.log(this.name);
}
const user = {
name: "Софія"
};
const result = printName.call(user);
console.log(result);
// Софія
// undefinedundefined у цьому випадку — результат роботи printName, а не результат console.log.
thisArgПерший аргумент call або apply називається thisArg. Він стає значенням this під час виклику функції.
function getName() {
return this.name;
}
const user = {
name: "Олексій"
};
console.log(getName.call(user));
// Олексійnull та undefinedПоведінка null та undefined залежить від режиму виконання функції.
У суворому режимі this зберігає передане значення:
"use strict";
function showThis() {
return this;
}
console.log(showThis.call(null));
// nullУ нестрогому режимі JavaScript може замінити null або undefined глобальним об’єктом. Покладатися на таку поведінку не варто. Якщо функції потрібен конкретний контекст, передавайте об’єкт явно.
Якщо передати примітивне значення, наприклад рядок або число, у нестрогому режимі JavaScript може тимчасово обгорнути його у відповідний об’єкт. У строгому режимі this залишиться примітивом.
"use strict";
function getType() {
return typeof this;
}
console.log(getType.call("JavaScript"));
// stringНа практиці як thisArg зазвичай передають об’єкт.
call та apply з функціями-стрілкамиФункції-стрілки не мають власного this. Вони беруть this із зовнішньої області видимості, тому call та apply не можуть змінити його.
const user = {
name: "Наталія"
};
const arrowFunction = () => this.name;
console.log(arrowFunction.call(user));
// значення залежить від зовнішнього контексту, але не "Наталія"Звичайна функція реагує на call та apply:
function regularFunction() {
return this.name;
}
const user = {
name: "Наталія"
};
console.log(regularFunction.call(user));
// НаталіяТому для динамічного контексту використовуйте звичайні функції, а не стрілки.
Створимо функцію, яка форматує інформацію про товар. Дані для форматування зберігатимуться в контексті this.
function formatProduct(currency, includeStock) {
let result = `${this.name}: ${this.price} ${currency}`;
if (includeStock) {
result += `, доступно: ${this.stock}`;
}
return result;
}
const product = {
name: "Механічна клавіатура",
price: 3200,
stock: 14
};
console.log(formatProduct.call(product, "грн", true));
// Механічна клавіатура: 3200 грн, доступно: 14
console.log(formatProduct.apply(product, ["грн", false]));
// Механічна клавіатура: 3200 грнТут:
product стає значенням this;
"грн" — перший параметр функції;
true або false — другий параметр;
call отримує параметри окремо;
apply отримує їх у масиві.
call та applycall і apply змінюють this лише для одного конкретного виклику.
function showName() {
return this.name;
}
const firstUser = {
name: "Анна"
};
const secondUser = {
name: "Богдан"
};
console.log(showName.call(firstUser));
// Анна
console.log(showName.call(secondUser));
// БогданСама функція showName не запам’ятовує жоден із цих об’єктів.
Якщо потрібно створити нову функцію із закріпленим контекстом, використовуйте bind:
function showName() {
return this.name;
}
const user = {
name: "Катерина"
};
const showUserName = showName.bind(user);
console.log(showUserName());
// Катеринаbind не викликає функцію одразу. Він повертає нову функцію із закріпленим this.
thisЯкщо функція є стрілковою, call та apply не допоможуть змінити її контекст. У такому випадку потрібно змінити саму функцію на звичайну або перебудувати логіку.
const user = {
name: "Віктор,
showName() {
return this.name;
}
};
const showName = user.showName;
console.log(showName());
// undefined або помилка залежно від режимуПісля присвоєння user.showName змінній виклик більше не відбувається як виклик методу user.
Правильний варіант:
const result = user.showName.call(user);
console.log(result);
// Вікторcallcall очікує окремі аргументи:
function sum(a, b, c) {
return a + b + c;
}
const values = [1, 2, 3];
console.log(sum.call(null, values));
// 01,2,3У цьому випадку весь масив переданий як перший аргумент a.
Потрібно використати apply:
console.log(sum.apply(null, values));
// 6Або оператор розпилення:
console.log(sum.call(null, ...values));
// 6apply поверне нову функціюapply одразу запускає функцію:
function greet() {
return "Привіт";
}
const result = greet.apply(null);
console.log(result);
// ПривітЯкщо потрібна нова функція, використовуйте bind:
const newFunction = greet.bind(null);
console.log(newFunction());
// Привітthis стрілкової функціїconst getName = () => this.name;
const user = {
name: "Леся"
};
console.log(getName.call(user));
// не "Леся"Стрілкова функція не використовує this, переданий через call або apply.
У цьому виклику перший аргумент — контекст, а не перший параметр функції:
function greet(message) {
return `${message}, ${this.name}`;
}
const user = {
name: "Денис"
};
console.log(greet.call(user, "Добрий день"));
// Добрий день, ДенисЯкщо написати greet.call("Добрий день"), рядок буде використано як this, а параметр message залишиться undefined.
call, apply та bindcall — потрібно викликати функцію зараз і передати аргументи окремо.
apply — потрібно викликати функцію зараз і передати аргументи масивом.
bind — потрібно отримати нову функцію із заздалегідь заданим this і, за потреби, частиною аргументів.
Приклад розподілу відповідальності:
function logEvent(prefix, eventName) {
return `${prefix}: ${this.service} -> ${eventName}`;
}
const context = {
service: "user-service"
};
const immediateCall = logEvent.call(context, "INFO", "login");
const argumentsList = ["ERROR", "logout"];
const immediateApply = logEvent.apply(context, argumentsList);
const boundLogger = logEvent.bind(context, "DEBUG");
const delayedCall = boundLogger("profile-update");
console.log(immediateCall);
// INFO: user-service -> login
console.log(immediateApply);
// ERROR: user-service -> logout
console.log(delayedCall);
// DEBUG: user-service -> profile-updatecall та apply негайно викликають функцію.
Перший аргумент обох методів визначає значення this.
call приймає аргументи окремими значеннями.
apply приймає аргументи одним масивом.
Обидва методи повертають результат виклику функції.
Стрілкові функції ігнорують контекст, переданий через call та apply.
Для виклику функції з контекстом на один раз підходять call або apply.
Для створення нової функції із закріпленим контекстом використовуйте bind.
Якщо аргументи зберігаються в масиві, apply можна замінити на call з оператором розпилення.