Пойми, как this определяется динамически, как работает цепочка прототипов и что на самом деле классы добавляют к объектной модели.
Открыть этот урок в KodokonЗначение this решается в месте вызова, а не при объявлении. Четыре правила по убыванию приоритета: вызов через new, явная привязка (call, apply, bind), вызов как метода (obj.fn()) и обычный вызов (fn()), который в строгом режиме даёт undefined. Отсюда напрямую следует самый частый баг из практики: если вытащить метод из его объекта, неявная привязка ломается.
const user = {
name: "Ada",
greet() {
return "Hello, " + this.name;
},
};
console.log(user.greet()); // "Hello, Ada"
const detached = user.greet;
// detached(); // TypeError: this is undefined
const bound = user.greet.bind(user);
console.log(bound()); // "Hello, Ada"У стрелочных функций нет своего this: они захватывают его лексически из охватывающей области, ровно как замыкание. Это делает их идеальными для колбэков, но вредными в роли методов объектных литералов, где this будет указывать на внешнюю область. Под капотом каждый объект делегирует через свою цепочку прототипов: отсутствующее свойство ищется звено за звеном, пока не будет достигнут null.
function Animal(name) {
this.name = name;
}
Animal.prototype.describe = function () {
return this.name + " is an animal";
};
const dog = new Animal("Rex");
console.log(dog.describe()); // "Rex is an animal"
console.log(Object.getPrototypeOf(dog) ===
Animal.prototype); // true
console.log(dog.hasOwnProperty("describe")); // falseСинтаксис class не меняет эту модель: он делает её читаемой. Методы, объявленные в теле класса, попадают на Constructor.prototype и потому разделяются. Поля экземпляра (включая стрелочные функции, присвоенные как поля) пересоздаются для каждого экземпляра: привязка this гарантирована, но расход памяти умножается. Наконец, поля #private дают приватность, которую обеспечивает сам движок, в отличие от договорённости с _подчёркиванием.
class BankAccount {
#balance = 0;
deposit(amount) {
if (amount <= 0) throw new Error("Invalid amount");
this.#balance += amount;
return this;
}
get balance() {
return this.#balance;
}
}
const account = new BankAccount();
account.deposit(50).deposit(25);
console.log(account.balance); // 75this в строгом режиме при обычном вызове fn() без объекта и без явной привязки?this?