В чем разница между call и apply в JavaScript? Когда что использовать?
Основное различие: способ передачи аргументов
Вы абсолютно правы — ключевое отличие между call и apply кроется в том, как передаются аргументы функции после контекста this:
- call(thisArg, arg1, arg2, ...) — аргументы передаются через запятую, по отдельности.
- apply(thisArg, [argsArray]) — аргументы передаются в виде массива (или массивоподобного объекта).
Это не просто «синтаксический сахар» — это два разных инструмента для разных ситуаций. Рассмотрим практические примеры.
Когда использовать call?
call удобен, когда количество аргументов известно заранее и их немного. Например, при заимствовании методов:
const obj = { 0: 'a', 1: 'b', length: 2 };
// Заимствуем метод push у Array
Array.prototype.push.call(obj, 'c', 'd');
console.log(obj); // { 0: 'a', 1: 'b', 2: 'c', 3: 'd', length: 4 }
Здесь мы передаем аргументы по одному — это естественно и читаемо.
Когда использовать apply?
apply незаменим, когда аргументы уже хранятся в массиве или когда их количество динамическое. Классический пример — нахождение максимума в массиве:
const numbers = [5, 2, 9, 1, 7];
const max = Math.max.apply(null, numbers);
console.log(max); // 9
Без apply пришлось бы писать Math.max(numbers[0], numbers[1], ...), что невозможно при неизвестной длине массива.
Работа с псевдомассивом arguments
До появления rest-параметров (...args) apply был единственным способом передать все аргументы функции в другую функцию:
function sumAll() {
// arguments — псевдомассив, у него нет методов массива
// Используем apply, чтобы передать все аргументы в reduce
return Array.prototype.reduce.apply(arguments, [
(acc, val) => acc + val,
0
]);
}
console.log(sumAll(1, 2, 3)); // 6
Хотя сегодня чаще используют Array.from(arguments) или spread-оператор, понимание apply помогает читать старый код.
Производительность и современные альтернативы
В современных движках разница в производительности между call и apply пренебрежимо мала. Однако есть важный нюанс: apply может быть медленнее при работе с очень большими массивами аргументов (более 65536 элементов), так как движок может выбросить исключение RangeError.
Сегодня многие разработчики предпочитают spread-оператор как более читаемую альтернативу apply:
// Вместо apply
Math.max.apply(null, numbers);
// Используем spread
Math.max(...numbers);
// Вместо apply для передачи контекста
func.apply(ctx, args);
// Используем call + spread
func.call(ctx, ...args);
Итог: краткое правило
- call — когда знаете аргументы поимённо (фиксированное количество).
- apply — когда аргументы уже в массиве/псевдомассиве или их количество динамическое.
- spread + call — современный стиль, заменяющий apply в большинстве случаев.
Главное — помните, что оба метода мгновенно вызывают функцию с заданным this. Если нужно не вызывать, а привязать контекст для последующего вызова — используйте bind.
Дополнение: bind как третий вариант
Коллега выше отлично объяснил разницу между call и apply. Хочу добавить, что часто путаницу вызывает ещё один метод — bind. В отличие от call/apply, которые немедленно вызывают функцию, bind создаёт новую функцию с привязанным контекстом, которую можно вызвать позже:
const person = { name: 'Анна' };
function sayHi(greeting) {
console.log(`${greeting}, ${this.name}!`);
}
// Немедленный вызов
sayHi.call(person, 'Привет'); // Привет, Анна!
// Создание новой функции с привязанным контекстом
const boundSayHi = sayHi.bind(person, 'Здравствуйте');
boundSayHi(); // Здравствуйте, Анна!
Когда bind удобнее?
В обработчиках событий или колбэках, где контекст теряется:
const button = document.querySelector('button');
const user = { name: 'Олег' };
// Потеря контекста
button.addEventListener('click', function() {
console.log(this.name); // undefined (this = button)
});
// Решение с bind
button.addEventListener('click', function() {
console.log(this.name);
}.bind(user)); // Олег
Запомните тройку: call (список аргументов), apply (массив аргументов), bind (отложенный вызов). Каждый для своей задачи.
Ответов пока нет.
Войдите чтобы оставить ответ