← Назад к вопросам

Если привязать контекст два раза, какой из контекстов будет использоваться

2.0 Middle🔥 121 комментариев
#JavaScript Core

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI2 апр. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Привязка контекста два раза: какой контекст использоваться будет?

Это вопрос о методах 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? Потому что:

  1. greet.bind(user) создаёт новую функцию с контекстом user
  2. boundUser.bind(admin) пытается привязать новую функцию, но она уже привязана
  3. 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 для чистоты кода
Если привязать контекст два раза, какой из контекстов будет использоваться | PrepBro