Learning Book

Стрелочные функции и this

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

Стрелочные функции не имеют собственного this. Они берут this из лексического окружения — из того места, где написаны, а не откуда вызваны.

class Timer {
  constructor() {
    this.count = 0
  }

  start() {
    // Стрелочная функция захватывает this из start()
    // this в start() === экземпляр Timer
    setInterval(() => {
      this.count++ // this === Timer — работает!
      console.log(this.count)
    }, 1000)
  }
}

const timer = new Timer()
timer.start() // 1, 2, 3...

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

setInterval(function() {
  this.count++ // this === undefined (strict) или window
}, 1000)
// Ошибка: Cannot set properties of undefined

Стрелку нельзя переопределить

call, apply, bind не могут изменить this стрелочной функции:

const arrow = () => this  // this из глобального/модульного контекста

arrow.call({ x: 1 })    // не меняет this
arrow.bind({ x: 2 })()  // не меняет this
new arrow()              // TypeError: arrow is not a constructor

Когда НЕ использовать стрелочные функции

1. Методы объекта

const obj = {
  name: 'Объект',
  // ПЛОХО: стрелка берёт this из глобального контекста
  badMethod: () => {
    console.log(this?.name) // undefined — this не obj!
  },
  // ХОРОШО: обычная функция
  goodMethod() {
    console.log(this.name) // "Объект"
  }
}

obj.badMethod()  // undefined
obj.goodMethod() // "Объект"

2. Методы прототипа

class Counter {
  count = 0

  // ПЛОХО: стрелка в прототипе не работает правильно
  // (на самом деле стрелки в классе создаются в конструкторе, не в прототипе)
  increment = () => {
    this.count++ // работает, но копируется в КАЖДЫЙ экземпляр
  }

  // ЛУЧШЕ: обычный метод — один в прототипе для всех экземпляров
  decrement() {
    this.count--
  }
}

3. Обработчики событий, где нужен this === элемент

button.addEventListener('click', function() {
  this.textContent = 'Нажато!' // this === button ✓
})

button.addEventListener('click', () => {
  this.textContent = 'Нажато!' // this !== button ✗
})

4. Функции-конструкторы

// Стрелки нельзя использовать как конструкторы
const Foo = () => {}
new Foo() // TypeError: Foo is not a constructor
Под капотом стрелочная функция при создании записывает текущее значение this в своё замыкание. Когда стрелку вызывают, движок просто читает это сохранённое значение — без поиска в цепочке вызовов. call/apply/bind технически выполняются, но шаг «установить this» пропускается, потому что у стрелки нет своего [[ThisMode]].

Идеальные случаи для стрелочных функций

// Колбэки в методах — часто нужен this метода
class DataService {
  constructor() {
    this.data = []
    this.prefix = 'LOG'
  }

  fetchAndProcess(items) {
    // Стрелки в map/filter/reduce — отличный выбор
    return items
      .filter(item => item.active)     // this не нужен
      .map(item => this.transform(item)) // this === DataService ✓
  }

  transform(item) {
    return { ...item, prefix: this.prefix }
  }
}