Что такое this в JavaScript
hard

Что такое this в JavaScript

Это «это», но что именно означает — зависит от контекста

Ключевое слово this — одна из особенностей JavaScript. Изначально оно пришло из Java, чтобы помочь в реализации объектно-ориентированного программирования. А особенность в том, что это слово может означать разные объекты в зависимости от того, где оно написано. Разбираемся, как это работает, на примерах с кодом.

Что такое this

В JavaScript this — это ссылка на какой-то объект. Особенность в том, что объект, на который ссылается this, может меняться в зависимости от контекста вызова. Это как показать пальцем на что-то в коде и сказать «Вот, я имею в виду вот этот объект». Хитрость в том, что «вот это» в коде может быть разным — и всё зависит от того, кто хочет использовать этот объект.

Чаще всего this определяется тем, кто вызывает функцию. В этом его отличие от области видимости, которая определяется местом вызова функции. Из-за этого в коде часто можно встретить много вызовов this, только ссылаться они будут на разные объекты.

Теперь посмотрим, как меняется значение this в разных ситуациях: от простого вызова без контекста до сложных функций.  

Простой вызов

В общем случае this указывает на глобальный объект. Для браузера такой объект — это само окно браузера, поэтому если набрать в консоли браузера 

console.log(this);

то мы увидим, что здесь this привязано к глобальному контексту, а именно к объекту window в браузере.

Когда в JS включён строгий режим, то правила меняются. Если функция вызывается в строгом режиме use strict, то this будет равно undefined. Про строгий режим поговорим в другой раз; если что, по умолчанию он выключен, поэтому this можно использовать даже в простом вызове.

This ссылается на объект

Когда this используется внутри объекта, то ссылается на сам объект. Допустим, мы создали объект dog с тремя методами и используем в одном из его методов this. Так как this внутри метода указывает на текущий объект, в контексте которого метод был вызван, то в нашем примере мы получим ссылку на объект dog.

Напишем простой код с объектом и выведем в консоль значение this:

var dog = {
  name: 'Cheems',
  breed: 'Shiba Inu',
  intro: function(){
    console.log(this);
  }
};

dog.intro();

В консоли мы увидим представление объекта со всеми методами:

Использование this делает код более гибким. Значение this само привязывается к объекту, в контексте которого он вызван. Это позволяет методу работать с данными конкретного экземпляра объекта и делать код более универсальным.

Режим конструктора 

Конструктор — это функция, которую используют, чтобы создавать однотипные объекты. Название конструктора в JavaScript должно быть существительным и начинаться с большой буквы. Конструкторы вызывают с помощью ключевого слова new. Задача конструктора — объяснить компьютеру, с какими параметрами и свойствами нужно будет создавать новый объект.

Сделаем конструктор и создадим новый объект. Обратите внимание, как мы используем this: оно автоматически указывает на объект в текущем конструкторе, откуда его вызывают:

function Dog () {
// this привязывается к новому объекту
  this.name = 'Cheems'
}

// создаём новый объект Dog
const firstDog = new Dog()
firstDog.name === 'Cheems'

console.log(firstDog.name);

Такой подход удобен тем, что с помощью this и единственной функции-конструктора мы можем создавать множество однотипных объектов и указывать напрямую, к какому объекту обращаемся. 

Покажем, как это работает, на примере функции-конструктора для создания объектов автомобилей:

function Car(brand, model, year) {

// используем this, чтобы установить свойства объектов
  this.brand = brand;
  this.model = model;
  this.year = year;
}

// создаём новые объекты с помощью функции-конструктора
const car1 = new Car('Toyota', 'Camry', 2022);
const car2 = new Car('Honda', 'Accord', 2021);
const car3 = new Car('Ford', 'Mustang', 2020);

console.log(car1); 
console.log(car2); 
console.log(car3); 

Если бы мы писали подобную логику без использования this, то сначала бы создали новый объект, установили его свойства, вернули объект и уже после этого создавали новые. Тогда код получился бы более громоздким.

Работа с методами call() и apply()

В JS объекты чаще всего обладают собственными свойствами и методами, при этом разные объекты не могут просто так использовать методы друг друга. Но иногда нужно обойти это ограничение. Для этого используют методы call() и apply() — они позволяют вызывать нужные функции в контексте других объектов. Чтобы передать нужный контекст в функцию, как раз и используют this — он хранит контекст вызова, который не меняется во время передачи в другую функцию. 

Вызов функции через методы call() или apply() называют непрямым вызовом.

В синтаксисе методов есть небольшое различие: call() принимает аргументы списком через запятую, apply() принимает массив аргументов. В остальном они работают плюс-минус одинаково.

Для наглядности сделаем так:

  1. Создадим функцию, которая берёт в качестве аргумента приветственное слово и смайлик.
  2. Внутри этой функции используем this — это поможет нам получить контекст того объекта, который вызвал эту функцию.
  3. Создадим две переменные с именами.
  4. Вызовем функцию приветствия, используя переменные с именами как аргументы.
  5. Так как мы использовали this внутри функции, она получит нужный контекст: перенесёт его из переменных с именами.
  6. В итоге функция приветствия по контексту поймёт, что нам нужно взять имена из переменных и добавить их в вывод в консоль.

function greet(greetWord, emoticon) {
  console.log(`${greetWord} ${this.name} ${emoticon}`)
}

const user1 = { name: 'Barbie' }
const user2 = { name: 'Ken' }

greet.call(user1, 'Hi,', ':-)')
greet.call(user2, 'Hello,', ':-D')
greet.apply(user1, ['Hi,', ':-)'])
greet.apply(user2, ['Hello,', ':-D']) 

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

Стрелочная функция не создаёт свой контекст исполнения, а берёт его из своей внешней функции, в которой эта стрелочная функция определена. 

Создадим функцию, которая выводит приветствие и имя пользователя, а затем спустя некоторое время повторяет приветствие:

function greetWaitAndAgain() {
  console.log(`Hi, ${this.name}!`)
  setTimeout(() => {
    console.log(`Hi again, ${this.name}!`)
  })
}

// создаём объект
const user = { name: 'Barbie' }

// добавляем метод к объекту
user.greetWaitAndAgain = greetWaitAndAgain;
user.greetWaitAndAgain()

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

Как понять, чему равно значение this

Определяем, чему равно this в каждом конкретном случае:

  • Если мы не находимся внутри функции, то this равно глобальному объекту — окну браузера.
  • Если функция получена как свойство объекта и сразу же используется, то this будет равно этому объекту.
  • Если функция вызывается с помощью оператора new, то this будет ссылаться на вновь созданный объект в конструкторе функции
  • Если функция создана с помощью метода call или apply, то значение this будет аргументом этой функции
  • Если мы внутри стрелочной функции, то this равно значению this, находящемуся вне этой функции. 

Зачем такие сложности?

Ключевое слово this хорошо раскрывает динамическую природу JavaScript, даёт большую гибкость и сокращает дублирование кода. Например, с помощью this можно писать очень абстрактные функции, которые будут использовать контекст выполнения для своей работы. Так мы можем добиться полиморфизма, а оттуда уже до ООП недалеко.

Обложка:

Алексей Сухов

Корректор:

Ирина Михеева

Вёрстка:

Мария Дронова

Соцсети:

Юлия Зубарева

Получите ИТ-профессию
В «Яндекс Практикуме» можно стать разработчиком, тестировщиком, аналитиком и менеджером цифровых продуктов. Первая часть обучения всегда бесплатная, чтобы попробовать и найти то, что вам по душе. Дальше — программы трудоустройства.
Вам может быть интересно
hard
[anycomment]
Exit mobile version