Если привязать контекст два раза, какой из контекстов будет использоваться
Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
Привязка контекста два раза: какой контекст использоваться будет?
Это вопрос о методах bind(), call() и apply() в JavaScript. Когда привязываешь контекст несколько раз, будет использован первый привязанный контекст, потому что привязка необратима.
bind() возвращает новую функцию
bind() возвращает новую функцию с привязанным контекстом. Это критически важно:
const user = { name: "Alice" };
const admin = { name: "Bob" };
function greet() {
console.log(this.name);
}
const boundUser = greet.bind(user);
const boundAdmin = boundUser.bind(admin);
boundAdmin(); // "Alice", не "Bob"
Почему Alice? Потому что:
greet.bind(user)создаёт новую функцию с контекстом userboundUser.bind(admin)пытается привязать новую функцию, но она уже привязана- bind игнорирует попытку переривязать уже привязанную функцию
Почему это происходит?
Внутри JavaScript, когда функция уже привязана через bind(), повторное применение bind() игнорирует новый контекст:
function sayHello() {
return `Hello, ${this.name}`;
}
const user = { name: "Alice" };
const admin = { name: "Bob" };
// Первая привязка
const hello1 = sayHello.bind(user);
console.log(hello1()); // "Hello, Alice"
// Вторая привязка на result первой привязки
const hello2 = hello1.bind(admin);
console.log(hello2()); // "Hello, Alice" <- Всё ещё Alice!
А с call() и apply()?
call() и apply() - это разные методы, они НЕ привязывают контекст. Они вызывают функцию с нужным контекстом один раз:
function introduce() {
console.log(`Я ${this.name}`);
}
const user = { name: "Alice" };
const admin = { name: "Bob" };
// call() и apply() не привязывают, просто вызывают
introduce.call(user); // "Я Alice"
introduce.call(admin); // "Я Bob"
introduce.apply(user); // "Я Alice"
Если применить call() несколько раз - каждый раз используется нужный контекст:
const func = function() {
return this.value;
};
const obj1 = { value: 10 };
const obj2 = { value: 20 };
console.log(func.call(obj1)); // 10
console.log(func.call(obj2)); // 20
console.log(func.call(obj1)); // 10 (снова obj1)
Полный пример с bind
const user = {
name: "Alice",
greet: function() {
console.log(`Hello, I'm ${this.name}`);
}
};
const admin = { name: "Bob" };
// Извлекаем метод и привязываем контекст
let greeting = user.greet; // теряем контекст
greeting(); // undefined (this = window или undefined в strict mode)
// Привязываем к user
greeting = user.greet.bind(user);
greeting(); // "Hello, I'm Alice"
// Попытаемся переривязать к admin
const newGreeting = greeting.bind(admin);
newGreeting(); // "Hello, I'm Alice" <- Контекст user остаётся!
Почему это важно в практике?
Это часто встречается в обработчиках событий:
class Button {
constructor(label) {
this.label = label;
}
onClick() {
console.log(`Button "${this.label}" clicked`);
}
}
const btn = new Button("Save");
const element = document.querySelector("button");
// Неправильно: теряется контекст
element.addEventListener("click", btn.onClick); // undefined
// Правильно: привязываем контекст
element.addEventListener("click", btn.onClick.bind(btn));
// "Button Save clicked"
// Если повторно привязать, изменений не будет
const boundHandler = btn.onClick.bind(btn);
const reboundHandler = boundHandler.bind(new Button("Delete"));
element.addEventListener("click", reboundHandler);
// "Button Save clicked" <- Остаётся первоначальное имя
React пример
В React классовых компонентах часто использовали bind():
class Counter extends React.Component {
constructor(props) {
super(props);
this.state = { count: 0 };
// Привязываем контекст один раз в конструкторе
this.increment = this.increment.bind(this);
}
increment() {
this.setState({ count: this.state.count + 1 });
}
render() {
return (
<button onClick={this.increment}>
Count: {this.state.count}
</button>
);
}
}
Если попытаться переривязать в render():
render() {
return (
<button onClick={this.increment.bind(this).bind(someOtherContext)}>
Считать
</button>
);
}
this остаётся компонентом, переривязка не сработает.
Итог
- bind() привязывает контекст один раз и навсегда
- Повторное применение bind() игнорируется
- call() и apply() просто вызывают функцию, не привязывают
- Первый bind() выигрывает над всеми последующими
- В React 16.8+ используй стрелочные функции вместо bind для чистоты кода