Методы и this

Методы и this #

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


Метод в литерале объекта #

const cart = {
  id: 'cart-7a2',
  currency: 'RUB',
  items: [],

  addItem(product, qty = 1) {
    this.items.push({ sku: product.sku, qty, price: product.price })
    return this
  },

  itemCount() {
    return this.items.reduce((sum, line) => sum + line.qty, 0)
  },
}

cart.addItem({ sku: 'kb-01', price: 1490 }, 2)
cart.itemCount() // 2

Сокращённая запись addItem() { } эквивалентна addItem: function() { } — для методов это предпочтительный синтаксис.


Как определяется this #

this не привязан к месту объявления функции (кроме стрелок). Он задаётся способом вызова:

Вызовthis (упрощённо)
obj.method()obj
method()undefined в strict, иначе глобальный объект
fn.call(ctx, ...)явно переданный ctx
стрелочная функцияthis из внешней области
const playlist = {
  title: 'Focus Tracks',
  tracks: ['a', 'b'],

  describe() {
    return `${this.title}: ${this.tracks.length} треков`
  },
}

playlist.describe() // "Focus Tracks: 2 треков"

Потеря контекста #

Если метод передать как колбэк или вызвать отдельно от объекта, this теряется:

const profile = {
  userId: 'u-104',
  greet() {
    return `Привет, ${this.userId}`
  },
}

const fn = profile.greet
// fn() — TypeError в strict или this === global

Типичный случай — обработчик события или setTimeout:

const cart = {
  id: 'cart-7a2',
  flush() {
    console.log(`Сохраняем ${this.id}`)
  },
}

setTimeout(cart.flush, 100) // this не cart

bind #

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

setTimeout(cart.flush.bind(cart), 100)

call и apply #

Немедленный вызов с явным this; apply принимает аргументы массивом:

function formatLine(sku, qty) {
  return `${this.prefix}:${sku}×${qty}`
}

const ctx = { prefix: 'LINE' }
formatLine.call(ctx, 'kb-01', 2) // "LINE:kb-01×2"
formatLine.apply(ctx, ['ms-02', 1]) // "LINE:ms-02×1"

Стрелочные функции и методы #

Стрелки не имеют собственного this — берут лексическое значение из окружающего кода:

const apiClient = {
  baseUrl: '/api/v2',

  // плохо как метод: this зафиксирован при создании объекта
  brokenFetch: () => {
    return fetch(`${this.baseUrl}/cart`)
  },

  // корректно
  fetchCart() {
    return fetch(`${this.baseUrl}/cart`)
  },
}

Стрелки уместны внутри метода для коротких колбэков, где нужен this внешнего метода:

const playlist = {
  tracks: [{ id: 1, bpm: 120 }, { id: 2, bpm: 90 }],

  fastTracks() {
    return this.tracks.filter((t) => t.bpm >= 100)
  },
}

Подробнее о стрелках — в разделе функции.


this в конструкторах #

При вызове с new this — новый создаваемый объект. Это разбирается в Конструкторы и new.

function LineItem(sku, qty) {
  this.sku = sku
  this.qty = qty
}

const line = new LineItem('kb-01', 1)
line.sku // "kb-01"

Цепочка вызовов (fluent interface) #

Возврат this из метода позволяет выстраивать цепочку:

const cart = {
  items: [],
  add(product) {
    this.items.push(product)
    return this
  },
  clear() {
    this.items = []
    return this
  },
}

cart.add({ sku: 'a' }).add({ sku: 'b' }).clear()

Паттерн встречается в построителях запросов и UI-библиотеках; не злоупотребляйте, если читаемость страдает.


Методы и прототип #

Функции на объекте — собственные свойства. Общие методы для многих экземпляров часто выносят в прототип — см. раздел ООП. Литерал с методами подходит для единичных конфигов и простых моделей; фабрики и классы — когда экземпляров много.


Итог #

  • Метод — функция в свойстве объекта; this при вызове obj.method() — это obj.
  • Отдельная ссылка на метод теряет контекст — используйте bind или обёртку.
  • call / apply задают this и аргументы при разовом вызове.
  • Стрелки не подходят как методы с динамическим this; уместны как внутренние колбэки.
  • Возврат this включает цепочку вызовов.

См. также #

Часто спрашивают

Почему this «потерялся» в колбэке?
У обычной функции this зависит от способа вызова. Сохраните контекст через bind или используйте стрелочную функцию осознанно. Подробнее