Чем отличаются bind, apply и call?
Вопрос
Чем отличаются методы bind, apply и call, и когда какой использовать?
Короткий ответ
Все три метода позволяют явно задать значение this при вызове функции, но по-разному: call и apply вызывают функцию немедленно (различаясь только форматом передачи аргументов — через запятую или массивом), а bind не вызывает функцию, а возвращает новую функцию с навсегда привязанным this.
Подробный ответ
call(thisArg, arg1, arg2, ...)— вызывает функцию сразу, с указаннымthisи аргументами, перечисленными по одному.apply(thisArg, [arg1, arg2, ...])— делает то же самое, что иcall, но аргументы передаются единым массивом. Удобно, когда аргументы уже собраны в массив или их количество заранее неизвестно.bind(thisArg, arg1, ...)— не вызывает функцию. Вместо этого возвращает новую функцию, у которойthis(и, опционально, часть аргументов — частичное применение) зафиксированы навсегда и не могут быть переопределены даже повторнымcall/applyповерх неё.
Практическое правило: call/apply — для одноразового вызова с нужным контекстом прямо сейчас; bind — когда функцию нужно передать куда-то на будущее (обработчик события, колбэк), но с гарантированно правильным this.
Важный нюанс: если передать bind/call/apply стрелочной функции — они не окажут никакого эффекта на this, потому что у стрелочных функций нет собственного this, который можно было бы переопределить (см. вопрос про this и стрелочные функции).
Пример
const user = {
name: 'Alice',
greet(greeting, punctuation) {
console.log(`${greeting}, ${this.name}${punctuation}`)
}
}
const otherUser = { name: 'Bob' }
// call — аргументы через запятую, вызов немедленный
user.greet.call(otherUser, 'Привет', '!') // "Привет, Bob!"
// apply — аргументы массивом, вызов немедленный
user.greet.apply(otherUser, ['Привет', '!']) // "Привет, Bob!"
// bind — возвращает новую функцию, вызов откладывается
const greetBob = user.greet.bind(otherUser, 'Привет')
greetBob('!') // "Привет, Bob!" — вызывается позже, this уже зафиксирован
// Типичный случай использования bind — обработчик события
class Counter {
count = 0
increment() {
this.count++
}
}
const counter = new Counter()
// без bind при клике this внутри increment будет undefined/button
button.addEventListener('click', counter.increment.bind(counter))
Дополнительные вопросы
- Почему повторный
bindповерх уже привязанной функции не может изменить еёthis? - Как реализовать свой
bindчерез замыкание? - Чем
Function.prototype.applyудобен при вызове функции с переменным числом аргументов (например,Math.max(...array)vsMath.max.apply(null, array))?