Метод Function.prototype.apply
Function.prototype.apply викликає функцію з явно вказаним контекстом (this), передаючи їй аргументи у вигляді масиву. Це «близький брат» методу call, і відрізняються вони тільки способом передачі аргументів.
Теорія
TL;DR
applyвикликає функцію одразу і дозволяє вручну задатиthis.- Аргументи передаються масивом або масивоподібним об'єктом, а не через кому.
callробить те саме, але аргументи йдуть переліком через кому.bindнічого не викликає: він лише повертає нову функцію з «прив'язаним»this.- Класичні застосування:
Math.max.apply(null, nums)і «позичання» методів масиву дляarguments. - Сучасна заміна в багатьох випадках: spread-синтаксис
func(...args).
Швидкий приклад
function sum(a, b, c) {
return a + b + c;
}
const numbers = [1, 2, 3];
// аргументи вже в масиві, тому apply підходить ідеально
console.log(sum.apply(null, numbers)); // 6Синтаксис
func.apply(thisArg, [argsArray])thisArgце об'єкт, який станеthisвсередині функції;argsArrayце масив (або масивоподібний об'єкт) з аргументами.
Базове використання з явним контекстом:
function greet(city, job) {
console.log(`Я ${this.name} з міста ${city}, працюю як ${job}`);
}
const user = { name: 'Maria' };
greet.apply(user, ['Kyiv', 'Frontend-розробник']);
// Я Maria з міста Kyiv, працюю як Frontend-розробникТе саме через call:
greet.call(user, 'Kyiv', 'Frontend-розробник');Різниця тільки у способі передачі аргументів:
call-> через кому,apply-> масивом.
Робота з масивоподібними об'єктами
apply часто застосовують, коли аргументи вже зберігаються в масиві або схожій структурі (arguments, NodeList тощо). Це дозволяє «позичити» метод масиву для об'єкта, у якого власних методів масиву немає:
function logArgs() {
console.log(arguments);
// перетворюємо arguments на масив за допомогою Array.prototype.slice
const arr = Array.prototype.slice.apply(arguments);
console.log(arr);
}
logArgs(1, 2, 3, 4);
// arguments -> [1, 2, 3, 4]Тут apply дає змогу викликати метод масиву (slice) на об'єкті arguments, який масивом не є, але має length і числові індекси.
Math.max і Math.min через apply
Методи Math.max і Math.min не приймають масив, лише перелік чисел. apply елегантно вирішує цю проблему:
const nums = [10, 20, 5, 40];
console.log(Math.max.apply(null, nums)); // 40
console.log(Math.min.apply(null, nums)); // 5Аналог на ES6:
Math.max(...nums); // сучасний spread-синтаксисapply проти call і bind
| Метод | Що робить | Як передаються аргументи | Коли викликається |
|---|---|---|---|
| call | Викликає функцію із заданим this | Через кому | Одразу |
| apply | Викликає функцію із заданим this | У вигляді масиву | Одразу |
| bind | Створює нову функцію з «прив'язаним» this | Через кому | Пізніше (під час виклику) |
Підсумок по apply:
| Властивість | Опис |
|---|---|
| Призначення | Викликати функцію із заданим this |
| Спосіб передачі аргументів | Масив або масивоподібний об'єкт |
| Контекст виконання | Глобальний або заданий вручну |
| Коли використовувати | Коли аргументи вже зібрані в масив |
| Сучасна альтернатива | func(...args) (spread-синтаксис) |
Особливості thisArg
Якщо thisArg дорівнює null або undefined, то:
- у строгому режимі (
'use strict') ->this = undefined; - у нестрогому ->
this = window(абоglobalу Node.js).
function showThis() {
console.log(this);
}
showThis.apply(null); // window (у нестрогому режимі)Саме тому в прикладах на кшталт Math.max.apply(null, nums) перший аргумент безпечно ставлять null: контекст цим функціям не потрібен.
Типові помилки
- Плутають
callіapply.applyочікує один масив, томуsum.apply(null, 1, 2, 3)не спрацює, аsum.call(null, [1, 2, 3])передасть масив як перший аргумент. - Думають, що
applyповертає нову функцію. Її повертаєbind;applyвиконує функцію негайно і повертає її результат. - Розраховують на глобальний
thisу строгому режимі. У модулях і класах код виконується в строгому режимі, томуthisприnullзалишитьсяundefined. - Передають у
applyдуже великий масив. Кожен елемент стає окремим аргументом, тож на масивах у сотні тисяч елементів можна отриматиRangeError: Maximum call stack size exceeded. - Намагаються створити об'єкт через
apply.applyне працює зnew: для виклику конструктора з масивом аргументів потрібніReflect.construct(Ctor, args)абоnew Ctor(...args).
Коротка відповідь
Для співбесідиКоротка відповідь допоможе вам впевнено відповідати на цю тему під час співбесіди.