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

Как this попадает в функцию?

1.8 Middle🔥 301 комментариев
#JavaScript Core

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

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

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

Как this попадает в функцию и правила его определения

this — один из самых сложных концептов в JavaScript. Понимание того, как this определяется при вызове функции, критично для написания корректного кода. Значение this определяется НЕ в момент объявления функции, а в момент её вызова.

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

Есть чёткая иерархия правил, которые определяют значение this:

1. Явное указание: call(), apply(), bind()

Высший приоритет — явное связывание контекста:

const user = {
  name: 'John',
  greet: function() {
    console.log(`Hello, ${this.name}`);
  }
};

const admin = {
  name: 'Admin'
};

// call() — вызывает с явным this
user.greet.call(admin); // 'Hello, Admin'

// apply() — то же самое, но с массивом аргументов
user.greet.apply(admin); // 'Hello, Admin'

// bind() — создаёт новую функцию с привязанным this
const boundGreet = user.greet.bind(admin);
boundGreet(); // 'Hello, Admin'

2. Вызов через объект (Method Call)

Если функция вызывается как метод объекта, this — это объект:

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

person.sayName(); // 'Alice'
// this = person

const obj = {
  name: 'Bob',
  nested: {
    name: 'Charlie',
    method: function() {
      console.log(this.name);
    }
  }
};

obj.nested.method(); // 'Charlie'
// this = obj.nested (непосредственный левый объект)

// Но если сохранить метод в переменную
const method = obj.nested.method;
method(); // undefined
// this = undefined (или window в браузере)

3. Вызов как функция (Function Invocation)

Если функция вызывается просто, без объекта контекста:

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

greet(); // undefined (в strict mode) или window (в браузере)

4. Конструктор (new)

При вызове с new, this — это новый созданный объект:

function User(name) {
  this.name = name;
  console.log(this); // User { name: 'John' }
}

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

5. Arrow функции — особый случай

Arrow функции не имеют собственного this. Они берут this из окружающей области (lexical this):

const person = {
  name: 'David',
  
  // Обычная функция
  regularMethod: function() {
    console.log(this.name); // 'David'
  },
  
  // Arrow функция
  arrowMethod: () => {
    console.log(this.name); // undefined (this из outer scope)
  }
};

person.regularMethod(); // 'David'
person.arrowMethod();   // undefined

Практические примеры

Проблема с потерей контекста

class Button {
  constructor(name) {
    this.name = name;
  }
  
  // Потенциальная проблема
  handleClick() {
    console.log(`Clicked: ${this.name}`);
  }
}

const btn = new Button('Submit');

// Проблема: потеря контекста
const handler = btn.handleClick;
handler(); // undefined (this потеряется)

// addEventListener ещё хуже
document.addEventListener('click', btn.handleClick);
// this будет document, а не btn

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

class Button {
  constructor(name) {
    this.name = name;
    // Привязываем this один раз
    this.handleClick = this.handleClick.bind(this);
  }
  
  handleClick() {
    console.log(`Clicked: ${this.name}`);
  }
}

const btn = new Button('Submit');
const handler = btn.handleClick;
handler(); // 'Clicked: Submit' — работает!

Решение 2: Arrow функция в классе (полифилл)

class Button {
  constructor(name) {
    this.name = name;
  }
  
  // Arrow функция как свойство класса
  handleClick = () => {
    console.log(`Clicked: ${this.name}`);
  };
}

const btn = new Button('Submit');
const handler = btn.handleClick;
handler(); // 'Clicked: Submit' — работает!

Решение 3: Обёртка в обработчике

class Button {
  constructor(name) {
    this.name = name;
  }
  
  handleClick() {
    console.log(`Clicked: ${this.name}`);
  }
}

const btn = new Button('Submit');

// Обёртка в arrow функцию
document.addEventListener('click', (e) => {
  btn.handleClick();
});

Таблица приоритетов this

ПриоритетСпособ вызоваthis =Пример
1 (Высокий)call/apply/bindявно указанныйfn.call(obj)
1 (Высокий)newновый объектnew Constructor()
2Метод объектасам объектobj.method()
3 (Низкий)Обычный вызовundefined/windowfn()
СпециальныйArrow функцияконтекст области() => { this }

Закон лексического this (Arrow функции)

const config = {
  name: 'MyApp',
  
  regularCallback: function() {
    setTimeout(function() {
      console.log(this.name); // undefined (this = window)
    }, 100);
  },
  
  arrowCallback: function() {
    setTimeout(() => {
      console.log(this.name); // 'MyApp' (this из arrowCallback)
    }, 100);
  }
};

config.regularCallback();
config.arrowCallback();

Отладка this

function debugThis() {
  console.log('this is:', this);
  console.log('typeof this:', typeof this);
  console.log('this.constructor.name:', this.constructor.name);
}

debugThis.call({ name: 'explicit' });
debugThis(); // undefined или window

Лучшие практики

  1. Используйте arrow функции в коллбэках и внутри методов
  2. Явно привязывайте контекст через bind() при передаче методов как коллбэков
  3. Избегайте сохранения методов в переменные без привязки
  4. Помните о приоритетах правил определения this
  5. Используйте стрелочные функции в конструкторах для обработчиков событий

Заключение

this в JavaScript определяется в момент вызова функции по чётким правилам. Понимание этих правил — ключ к написанию надёжного кода и избежанию бага с потерей контекста. Arrow функции значительно упрощают жизнь, автоматически захватывая this из окружающей области.