Значение this
— одна из самых запутанных составляющих JavaSсript. this
может принимать различные значения в зависимости от того, где располагается.
В этой статье мы рассмотрим, где используется ключевое слово this
и какое значение оно может принимать.
Верхний уровень
Если мы используем this
на верхнем уровне, то в качестве его значения должны получить глобальный объект, так как this
не находится внутри какого-либо хост-объекта.
К примеру, если у нас есть console.log(this)
на верхнем уровне скрипта браузера, то мы должны увидеть объект window
в качестве значения this
. Можем подтвердить это, использовав:
console.log(this === window)
Внутри функций верхнего уровня
Если мы определяем обычную функцию и ссылку this
, то в таком случае можем получить два различных значения в зависимости от того, включен ли для нашего скрипта строгий режим.
this
принимает значение window
, если скрипт не находится в строгом режиме. Однако, если это не так, то значение будет undefined
.
Например, если у нас имеется:
'use strict';
function foo() {
console.log(this);
}
foo();
То мы увидим, что this
принимает значение undefined
.
И напротив, в случае:
function foo() {
console.log(this);
}
foo();
Видим, что значение this
— это window
из console.log
.
Внутри объектов
Внутри объекта this
должно принимать значение хост-объекта, когда находится внутри обычных методов объекта. Например, если у нас есть:
function fullName() {
return `${this.firstName} ${this.lastName}`;
}const jane = {
firstName: 'Jane',
lastName: 'Smith',
fullName
};
const alex = {
firstName: 'Alex',
lastName: 'Smith',
fullName
};console.log(jane.fullName());
console.log(alex.fullName());
В коде, который показан сверху, у нас присутствует обычная функция fullName
, которая ссылается на this.firstName
и this.lastName
и возвращает их комбинацию.
Тогда, если мы определим объекты jane
и alex
, поместим внутрь каждого функцию fullName
и вызовем их, как это было сделано выше, мы получим:
Jane Smith
из первого console.log
и:
Alex Smith
из второго console.log
.
Внутри конструкторов
В JavaScript конструкторы — это функции, которые используют оператор new
для создания нового экземпляра и изменения свойств this
.
Например, если мы запустим следующий код без использования строгого режима:
function Person(firstName, lastName) {
this.firstName = firstName;
this.lastName = lastName;
}Person('Joe', 'Smith');
const {
firstName,
lastName
} = window;
console.log(firstName, lastName);
Тогда в качестве вывода console.log
мы увидим Joe Smith
, поскольку this
— это window
, если наш скрипт не использует строгий режим и относится к верхнему уровню.
Таким образом, this
— это window
, а следовательно firstName
и lastName
добавляются к window
в качестве свойств.
Это одна из причин того, зачем нужен строгий режим. Мы не хотим случайно добавлять новые глобальные переменные путем добавления свойств к window
.
Если мы запустим код, приведенный выше, со строгим режимом, как здесь:
'use strict';
function Person(firstName, lastName) {
this.firstName = firstName;
this.lastName = lastName;
}Person('Joe', 'Smith');
const {
firstName,
lastName
} = window;
console.log(firstName, lastName);
… тогда мы получим ошибку “Cannot set property ‘firstName’ of undefined” (не удается установить свойство ‘firstName’ для undefined), поскольку this
в строгом режиме будет undefined
.
Нам нужно вот что: использовать оператор new
для вызова функций конструктора. Например, чтобы создать новый экземпляр Person
, понадобится сделать следующее:
function Person(firstName, lastName) {
this.firstName = firstName;
this.lastName = lastName;
}const person = new Person('Joe', 'Smith');
Тогда мы получим:
{
"firstName": "Joe",
"lastName": "Smith"
}
… в качестве значения Person
.
Если мы вызываем функцию конструктора с помощью оператора new
, то this
ссылается на текущий экземпляр функции конструктора.
Внутри классов
Поскольку классы JavaScript — это просто синтаксический сахар для функций конструктора, то всё, показанное выше для функций конструктора, применимо и к классам.
Например, этот код даст нам тот же результат, что и в предыдущем примере:
class Person {
constructor(firstName, lastName) {
this.firstName = firstName;
this.lastName = lastName;
}
}const person = new Person('Joe', 'Smith');
Person
должно оказаться:
{
"firstName": "Joe",
"lastName": "Smith"
}
Параметры constructor
будут такими же, как и параметры в функциях конструктора.
Однако, если мы попытаемся вызвать класс Person
без оператора new
, то получим ошибку.
Это замечательная особенность использования классов вместо функций конструктора.
Внутри стрелочных функций
Стрелочные функции не привязаны к this
, так что описанное выше к ним не относится.
К примеру, стрелочные функции верхнего уровня будут содержать window
в качестве значения this
независимо от того, используется строгий режим или нет.
const foo = () => {
console.log(this);
}
foo();
В следующем примере мы получим undefined undefined
для каждого console.log
:
const fullName = () => {
return `${this.firstName} ${this.lastName}`;
}const jane = {
firstName: 'Jane',
lastName: 'Smith',
fullName
};
const alex = {
firstName: 'Alex',
lastName: 'Smith',
fullName
};
console.log(jane.fullName());
console.log(alex.fullName());
Как видим, стрелочные функции не устанавливают значения this.firstName
и this.lastName
. this
— это просто window
, а не хост-объект, так что и то, и другое будет undefined
.
Заключение
this
принимает значение хост-класса или хост-объекта, если находится внутри метода, определенного с помощью традиционной функции. Стрелочные функции не связаны с this
, поэтому мы не можем ссылаться на this
, как это было с традиционными функциями.
Случайно вызвать функции конструктора, как обычную функцию, легко. Если это произойдет без строгого режим, то мы ненамеренно добавим к window
новые свойства. Поэтому лучше использовать классы, так как мы не можем вызывать классы без new
. Функции конструктора и классы эквивалентны.
Читайте также:
- Даты-заглушки в модульных тестах на JavaSсript
- Декораторы в JavaScript
- Введение в регулярные выражения в JavaScript
Перевод статьи John Au-Yeung: What is ‘this’ in JavaScript?