Методы и 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включает цепочку вызовов.