Перейти к содержимому

bind / call / apply

В одной фразе: call и apply вызывают функцию немедленно с заданным this; bind возвращает новую функцию с “прошитым” this (и опционально — частичными аргументами). На собесе просят реализовать полифил bind и объяснить, почему bind нельзя перебить.

// call vs apply: разница только в способе передачи аргументов
function introduce(greeting, punct) {
return `${greeting}, I'm ${this.name}${punct}`;
}
const person = { name: 'Alice' };
introduce.call(person, 'Hello', '!'); // 'Hello, I'm Alice!'
introduce.apply(person, ['Hi', '.']); // 'Hi, I'm Alice.'
// apply полезен когда аргументы уже в массиве;
// сегодня заменяется spread: introduce.call(person, ...args)
// bind: partial application + this-binding
function multiply(x, y) { return x * y; }
const double = multiply.bind(null, 2); // x=2 зафиксирован
double(5); // 10
double(10); // 20
// Привязка метода к объекту (для addEventListener)
class Button {
constructor(label) { this.label = label; }
click() { console.log(`Clicked: ${this.label}`); }
}
const btn = new Button('Submit');
document.addEventListener('click', btn.click.bind(btn)); // this = btn всегда
// bind "замораживает" this — его нельзя перебить
function show() { return this.x; }
const bound = show.bind({ x: 1 });
bound.call({ x: 99 }); // 1 — call проигнорирован
bound.bind({ x: 99 })(); // 1 — второй bind не помогает
// Полифил Function.prototype.bind (упрощённый):
Function.prototype.myBind = function (context, ...partial) {
const fn = this;
return function (...args) {
return fn.apply(context, [...partial, ...args]);
};
};
const f = show.myBind({ x: 42 });
f(); // 42

Итог: bind создаёт новую функцию при каждом вызове — учитывай это при removeEventListener: нужно хранить результат bind, иначе снять обработчик не получится.