this в обычных и стрелочных функциях

В обычных функциях this определяется в момент вызова (динамический контекст), в стрелочных — в момент создания функции (лексический контекст из окружающего кода).

Зачем нужно

this — одна из самых частых причин ошибок в JavaScript. Понимание разницы между обычными и стрелочными функциями позволяет правильно использовать контекст в методах классов, обработчиках событий, таймерах и callback-функциях.

Где используется

  • Методы объектов и классов — обычные функции с динамическим this
  • Обработчики событий — this указывает на элемент (обычная функция)
  • Таймеры и промисы — стрелочные функции для сохранения контекста
  • Методы массивов (map, forEach) — стрелочные, чтобы не потерять this

this в обычных функциях

// Зависит от того, КАК вызвана функция:

// 1. Вызов как метод объекта
const obj = {
  name: 'Объект',
  greet {
    return `Привет от ${this.name}`; // this = obj
  }
};
obj.greet; // "Привет от Объект"

// 2. Вызов как обычная функция
function show() {
  return this; // в strict mode — undefined; иначе window/global
}
show;

// 3. Вызов через new
function Person(name) {
  this.name = name; // this = новый объект
}
const p = new Person('Иван');

// 4. Явная привязка через call/apply/bind
function greet() { return this.name; }
greet.call({ name: 'Мария' }); // "Мария"
const bound = greet.bind({ name: 'Пётр' });
bound; // "Пётр"

this в стрелочных функциях

// Стрелочная функция НЕ имеет своего this
// Она берёт this из лексического окружения (где объявлена)

class Timer {
  constructor {
    this.seconds = 0;
  }

  start {
    // Стрелочная: this = экземпляр Timer
    setInterval(() => {
      this.seconds++; // работает корректно
    }, 1000);
  }
}

// Обычная функция в том же контексте:
class TimerBroken {
  start {
    setInterval(function {
      this.seconds++; // this = undefined (strict) или window!
    }, 1000);
  }
}

Потеря контекста и способы исправления

class Button {
  constructor(label) {
    this.label = label;
  }

  // Проблема: потеря this при передаче как callback
  handleClick {
    console.log(`Нажата: ${this.label}`);
  }
}

const btn = new Button('Сохранить');

// Плохо: this потеряется
document.addEventListener('click', btn.handleClick);

// Решение 1: bind
document.addEventListener('click', btn.handleClick.bind(btn));

// Решение 2: стрелочная обёртка
document.addEventListener('click', () => btn.handleClick);

// Решение 3: стрелочное поле класса (class field)
class ButtonFixed {
  constructor(label) {
    this.label = label;
  }
  handleClick = () => { // стрелочная функция как поле — всегда верный this
    console.log(`Нажата: ${this.label}`);
  };
}

Таблица правил

Как вызвана this
Метод объекта obj.fn obj
Обычный вызов fn undefined (strict) / window
new Fn Новый объект
fn.call(ctx) ctx
Стрелочная функция this из окружения
Обработчик события (обычная) Элемент DOM

Частые ошибки

  • Деструктурирование методаconst { greet } = obj; greet;this теряется; используйте bind или стрелочные поля.
  • Стрелочная функция как метод объектаconst obj = { name: 'A', greet: => this.name }this будет из внешнего контекста, не объекта.
  • Использование bind в render/рендер-методах — создаёт новую функцию на каждый рендер; выносите в конструктор или используйте class fields.

Связанные темы

Ресурсы


⚡ Источник: Как работает this в javascript · AsForJS

  • 📅 2023-05-07 · YouTube · ID: 4tg4qokVS9o
  • Тезисы:
    • Нормальная функция (любая, кроме стрелочной) имеет скрытый параметр this
    • Стрелочная функция этот параметр не имеет — при доступе к this поиск идёт по цепочке окружений до родительского нормального
    • Главный вопрос для отладки: в какой нотации вызвана функция? Dot — this = объект перед точкой; иначе по правилам call/apply/bind, new, или undefined
    • Возврат стрелки из метода навсегда фиксирует this момента определения
  • Цитата: «Никаких контекстов, ничего придумывать не надо. Всё просто и примитивно... Он максимально логичен и максимально прост.»

🎓 Источник: Функции, стрелочные функции, контексты

  • 📅 2018-09-27 · YouTube · ID: pn5myCmpV2U
  • Тезисы:
    • У function есть функциональный (аргументы, локальные) и объектный (this) контекст
    • У лямбды объектного контекста нет — this берётся из контекста выше
    • bind работает только для function, лямбды не привязываются