Метод Function.prototype.bind
Function.prototype.bind повертає нову функцію з назавжди прив'язаним контекстом this, а не викликає функцію одразу. Це третій із «методів керування контекстом» поряд із call та apply, і саме відкладений виклик робить його особливим.
Теорія
TL;DR
bindстворює нову функцію, у якоїthisзафіксований назавжди.- Оригінальна функція не змінюється:
bindповертає окрему копію. - Можна заздалегідь зафіксувати частину аргументів (partial application).
- Функція не виконується одразу, її викликають пізніше вручну.
- Прив'язаний
thisнеможливо перевизначити черезcallабоapply. - Виняток: виклик через
newігнорує прив'язаний контекст.
Швидкий приклад
function greet() {
console.log(`Привіт, я ${this.name}`);
}
const user = { name: 'Maria' };
const sayHi = greet.bind(user);
sayHi(); // Привіт, я MariaТут sayHi це нова функція, у якої this назавжди прив'язаний до user.
Синтаксис і що саме повертає bind
func.bind(thisArg, arg1, arg2, ...)thisArgце значення, яке станеthisвсередині функції під час виклику;arg1, arg2, ...це аргументи, які будуть підставлені «за замовчуванням»;- повертається нова функція, яку можна викликати пізніше.
Отже, bind створює нову функцію, у якої:
- назавжди закріплений контекст
this; - можна частково зафіксувати аргументи;
- сама функція не виконується одразу, а повертається для виклику згодом.
Втрата контексту в callback
Найчастіше bind потрібен саме для того, щоб зберегти this під час передачі методу як callback. Коли метод передають за посиланням, зв'язок з об'єктом губиться:
const user = {
name: 'Maria',
sayHi() {
console.log(`Привіт, я ${this.name}`);
}
};
setTimeout(user.sayHi, 1000); // this загублений -> "Привіт, я undefined"
setTimeout(user.sayHi.bind(user), 1000); // правильно -> "Привіт, я Maria"Те саме стосується обробників подій і колбеків у методах на кшталт map, forEach, then.
Часткове застосування аргументів
bind дозволяє «передзадати» не лише контекст, а й частину аргументів:
function introduce(city, job) {
console.log(`Я ${this.name} з міста ${city}, працюю як ${job}`);
}
const person = { name: 'Maria' };
const introduceMaria = introduce.bind(person, 'Kyiv');
introduceMaria('Frontend-розробник');
// Я Maria з міста Kyiv, працюю як Frontend-розробникТут перший аргумент ('Kyiv') зафіксовано заздалегідь, а решту передають під час виклику.
Контекст при цьому потрібен не завжди, тож для чистого partial application передають null:
function multiply(a, b) {
return a * b;
}
const double = multiply.bind(null, 2);
console.log(double(5)); // 10bind створив нову функцію, де a = 2 зафіксовано, і вона чекає тільки на другий аргумент b.
Відмінність від call і apply
| Метод | Що робить | Коли викликається | Як передаються аргументи |
|---|---|---|---|
| call | Викликає функцію із заданим this | Одразу | Через кому |
| apply | Те саме, але аргументи масивом | Одразу | Масивом |
| bind | Повертає нову функцію з «прив'язаним» this | Пізніше | Через кому |
Особливості bind і поведінка з new
bindне змінює оригінальну функцію, а повертає нову копію.- «Прив'язаний»
thisзмінити вже не можна, навіть черезcallабоapply. - Якщо прив'язану функцію викликати як конструктор (
new),thisвсередині неї буде новим об'єктом, а не прив'язаним контекстом.
function User(name) {
this.name = name;
}
const BoundUser = User.bind({ name: 'Maria' });
const u = new BoundUser('Alice');
console.log(u.name); // Alice, контекст прив'язки ігнорується при newТобто при new у bind є особливість: пріоритет має конструктор, а не прив'язаний this.
Підсумок:
| Властивість | Опис |
|---|---|
| Що робить | Прив'язує контекст this і/або частину аргументів |
| Що повертає | Нову функцію |
| Коли викликається | Пізніше (вручну) |
| Чи можна змінити контекст потім | Ні |
| Часткове застосування | Так |
| Часто використовується для | Callbacks, обробників подій, React-компонентів |
Типові помилки
- Чекають, що
bindвикличе функцію. Він лише повертає нову функцію; без пари дужок нічого не станеться. - Не зберігають результат.
user.sayHi.bind(user);окремим рядком безглуздий: оригінал не змінюється, треба присвоїти результат. - Намагаються перебити прив'язку через
call. Післяbindконтекст зафіксований, іbound.call(other)його не змінить. - Прив'язують у кожному рендері.
onClick={this.handle.bind(this)}створює нову функцію щоразу і ламає мемоізацію; краще прив'язати один раз або використати стрілкову функцію. - Прив'язують стрілкову функцію. У стрілок немає власного
this, томуbindна них не впливає.
Коротка відповідь
Для співбесідиКоротка відповідь допоможе вам впевнено відповідати на цю тему під час співбесіди.