Перейти к содержимому
Теория · Замыкания и this · урок 6 из 11

this

Подумайте о слове «я». В устах Анны оно означает Анну, в устах Боба — Боба. Само слово не помнит, кто его написал: его смысл решает тот, кто говорит сейчас.

Слово this в методе работает так же. Как вы уже знаете, метод, вызванный через точку, получает в this объект перед точкой. Но тот же метод, сохранённый в константе, свой объект теряет:

JavaScript
"use strict";

const user = {
    name: "Ann",
    greet() {
        return `Hi, ${this.name}`;
    }
};

console.log(user.greet()); // Hi, Ann

const greet = user.greet;
console.log(greet()); // ✗
TEXT
TypeError: Cannot read properties of undefined (reading 'name')

Это текст Node.js и Chrome; в Firefox и Safari сообщение звучит иначе. Первый вызов печатает Hi, Ann, второй останавливает программу.

this решает вызов

this определяется в момент вызова, а не там, где написана функция. Это частый вопрос на собеседовании: «Чему равен this?» Для обычной функции ответ начинается со слов «смотря как вызвали». Разберём три вызова.

Вызов через точку

В вызове user.greet() перед точкой стоит user, поэтому this равен user. «Я» говорит user, и this.name читает его имя.

Вызов без точки

Константа greet хранит ту же функцию, но вызов greet() идёт без точки, и объекта у него нет. Что попадёт в this, зависит от режима:

  • в строгом режиме this равен undefined, а чтение undefined.name останавливает программу с TypeError;
  • без "use strict" в this попадает глобальный объект — встроенный объект, общий для всей программы.

Во втором случае ошибки нет, но имени пользователя функция всё равно не видит.

Метод как колбэк

Метод теряет объект и тогда, когда сам метод передают колбэком. Вызов ["Bob"].forEach(user.greet) передаёт forEach только функцию, и forEach вызывает её без точки. Это как переслать чужое сообщение «я приду» без подписи: кто это «я», уже не понять.

Метод bind

У каждой функции есть метод bind. Вызов user.greet.bind(user) не выполняет функцию, а возвращает новую, у которой this всегда равен user, как бы её ни вызвали:

JavaScript
"use strict";

const user = {
    name: "Ann",
    greet() {
        return `Hi, ${this.name}`;
    }
};

const greet = user.greet.bind(user);
console.log(greet()); // Hi, Ann
console.log(["Bob", "Eve"].map(greet).join(" ")); // Hi, Ann Hi, Ann

Разберём по шагам:

  1. user.greet.bind(user) возвращает новую функцию, к которой навсегда привязан user.
  2. greet() вызывается без точки, но печатает Hi, Ann.
  3. map получает greet колбэком и вызывает её для каждого элемента без точки. this каждый раз равен user, поэтому оба результата — Hi, Ann.

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

У стрелочной функции нет своего this. Она берёт this из кода вокруг, из того места, где она написана. Своего «я» у неё нет: она говорит от имени того кода, внутри которого её написали. Поэтому она хорошо подходит как колбэк внутри метода:

JavaScript
"use strict";

const team = {
    name: "Red",
    members: ["Ann", "Bob"],
    labels() {
        return this.members.map((m) => `${m}-${this.name}`);
    }
};

console.log(team.labels().join(" ")); // Ann-Red Bob-Red

Стрелочная функция написана внутри метода labels, поэтому её this — это this метода, то есть team. Обычную функцию function (m) { … } на этом месте map вызвал бы без точки, и this в ней был бы undefined.

Здесь аналогия со словом «я» перестаёт работать: человек не может говорить чужим «я», а стрелочная функция только так и делает.

Частая ошибка

Метод в литерале объекта — так называют фигурные скобки { … } с ключами и значениями — сделали стрелочной функцией:

JavaScript
const box = {
    size: 3,
    getSize: () => this.size // ✗
};

console.log(box.getSize()); // undefined

Ошибки нет, но вместо 3 программа печатает undefined. Стрелочная функция берёт this из кода вокруг литерала, а не из объекта box. На верхнем уровне файла в Node.js this — пустой объект, поэтому this.size даёт undefined, а не ошибку. Вызов через точку этого не меняет. Если методу нужен свой объект, пишите его обычной функцией, например в короткой форме: getSize() { return this.size; }.

Главное

  • this решает вызов: через точку это объект перед точкой, без точки в строгом режиме — undefined.
  • Метод, сохранённый в переменной или переданный колбэком, теряет объект; bind возвращает функцию с навсегда привязанным this.
  • У стрелочной функции нет своего this: она берёт его из кода вокруг, поэтому подходит как колбэк в методе, но не как сам метод.

Подпишись, чтобы пройти курс

Курс бесплатный. Войди через Telegram и подпишись на канал — откроются все уроки.

Войти другим способом