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

Что такое связывание?

2.2 Middle🔥 191 комментариев
#JavaScript Core

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

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

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

Что такое связывание

Связывание (Binding) в JavaScript - это механизм определения значения this внутри функции. Это один из самых запутанных концептов для начинающих разработчиков, но он критически важен.

Проблема: this в JavaScript

Значение this зависит от того, КАК вызывается функция, а не где она определена:

const user = {
  name: 'Alice',
  greet: function() {
    console.log(this.name);
  }
};

user.greet(); // 'Alice' - this = user

const greet = user.greet;
greet();      // undefined - this = window или undefined

Почему? Потому что this определяется в runtime, когда функция вызывается, а не когда она определяется.

Правила определения this

1. Method call (вызов метода на объекте)

const user = {
  name: 'Bob',
  sayName() {
    console.log(this.name);
  }
};

user.sayName(); // this = user

2. Function call (прямой вызов функции)

function sayName() {
  console.log(this);
}

sayName(); // this = window (не strict mode) или undefined (strict mode)

3. Constructor call (с new)

function User(name) {
  this.name = name;
}

const user = new User('Alice');
// this = новый объект

4. Arrow function

const user = {
  name: 'Charlie',
  greet: () => {
    console.log(this); // this = внешний scope, НЕ user!
  }
};

user.greet(); // this = window/undefined

Проблема с callback'ами

const user = {
  name: 'Diana',
  friends: ['Alice', 'Bob'],
  listFriends() {
    this.friends.forEach(function(friend) {
      console.log(this.name + ' knows ' + friend);
      // this = undefined!
      // Вывод: undefined knows Alice
    });
  }
};

user.listFriends();

Почему? Потому что callback'и - это прямые вызовы функций, не методы на объекте.

Решения для связывания

Решение 1: bind()

Метод bind() создает новую функцию с привязанным this:

const user = {
  name: 'Eve',
  greet: function() {
    console.log(this.name);
  }
};

const boundGreet = user.greet.bind(user);
boundGreet(); // 'Eve'

const unbound = user.greet;
unbound(); // undefined

В class компонентах React:

class Counter extends React.Component {
  constructor(props) {
    super(props);
    this.state = { count: 0 };
    // Связываем функцию в constructor
    this.handleClick = this.handleClick.bind(this);
  }

  handleClick() {
    this.setState({ count: this.state.count + 1 });
  }

  render() {
    return <button onClick={this.handleClick}>{this.state.count}</button>;
  }
}

Решение 2: Arrow function

Arrow функции автоматически привязывают this к внешнему scope:

const user = {
  name: 'Frank',
  friends: ['Alice', 'Bob'],
  listFriends() {
    this.friends.forEach((friend) => {
      console.log(this.name + ' knows ' + friend);
      // this = user (правильно!)
    });
  }
};

user.listFriends();
// Frank knows Alice
// Frank knows Bob

В class компонентах React (modernный способ):

class Counter extends React.Component {
  state = { count: 0 };

  // Arrow function автоматически привязана
  handleClick = () => {
    this.setState({ count: this.state.count + 1 });
  };

  render() {
    return <button onClick={this.handleClick}>{this.state.count}</button>;
  }
}

Решение 3: call() и apply()

Эти методы вызывают функцию с явно указанным this:

function introduce(city) {
  console.log(`Hi, I'm ${this.name} from ${city}`);
}

const user = { name: 'Grace' };

// call(thisArg, arg1, arg2, ...)
introduce.call(user, 'New York');
// Hi, I'm Grace from New York

// apply(thisArg, [arg1, arg2, ...])
introduce.apply(user, ['London']);
// Hi, I'm Grace from London

Практический пример: Event listener

Проблема:

class Button {
  constructor(element) {
    this.element = element;
    this.clicks = 0;
  }

  handleClick() {
    this.clicks++;
    console.log(this.clicks);
  }

  attach() {
    this.element.addEventListener('click', this.handleClick);
    // ПРОБЛЕМА: this = element, не Button!
  }
}

Решение 1: bind()

attach() {
  this.element.addEventListener(
    'click',
    this.handleClick.bind(this)
  );
}

Решение 2: arrow function

attach() {
  this.element.addEventListener(
    'click',
    () => this.handleClick()
  );
}

Решение 3: class field

handleClick = () => {
  this.clicks++;
  console.log(this.clicks);
};

attach() {
  this.element.addEventListener('click', this.handleClick);
}

В React функциональные компоненты - нет проблемы

В функциональных компонентах нет this, поэтому нет проблемы связывания:

function Counter() {
  const [count, setCount] = useState(0);

  // Нет this, никакого связывания не нужно
  const handleClick = () => {
    setCount(count + 1);
  };

  return <button onClick={handleClick}>{count}</button>;
}

Это одна из причин, почему функциональные компоненты проще!

Сравнение методов

СпособПростотаПроизводительностьРекомендуется
bind() в constructorСредняяХорошоДа, для class
Arrow function fieldПростаяХорошоДа, для class
Inline arrow functionОчень простаяПлохо*Нет, создает новую функцию каждый render
Функциональные компонентыОчень простаяОтличноДа, рекомендуется

*В inline arrow функции создается новая функция на каждый render, что может вызвать unnecessary re-renders дочерних компонентов.

Заключение

Связывание (Binding) - это:

  1. Механизм определения this в функции
  2. Зависит от того, КАК функция вызывается
  3. Может быть явно контролировано через bind(), call(), apply()
  4. Arrow функции автоматически привязывают this
  5. В функциональных компонентах React проблемы нет

Понимание binding критично для написания корректного JavaScript кода, особенно при работе с callbacks и event listeners.