Понимание магии «этого» в JavaScript

Понимание магии «этого» в JavaScript

20 марта 2022 г.

Это то, что все время используется в Javascript, но часто то, к чему оно относится, остается загадкой. В Javascript это работает совершенно иначе, чем в других языках программирования, и работает по-разному в зависимости от того, используете ли вы строгий режим или нет.


Если вам трудно, вы не одиноки. Давайте посмотрим, как именно это работает, и устраним любую путаницу в отношении того, что это означает в различных контекстах.


Что это такое в Javascript


this — это ключевое слово в Javascript, которое относится к свойству или набору свойств в определенном контексте. Контекст, в котором мы используем this, изменяет его свойства. В глобальном контексте this относится к глобальному объекту, который в браузере является окном, но является globalThis в Node.JS и других реализациях Javascript.


```javascript


console.log(этот); // То же, что и console.log(window);


Вне каких-либо функций или кода это всегда так. Однако в разных местах это означает разное.


Это в функциях в Javascript


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


```javascript


console.log(этот); // То же, что и console.log(window);


функция моя функция () {


console.log(этот); // То же, что и console.log(window);


мояФункция();


Однако в строгом режиме this внутри функции не определено.


```javascript


"использовать строгий"


console.log(этот); // То же, что и console.log(window);


функция моя функция () {


console.log(этот); // Это не определено!


мояФункция();


Решение с помощью call()


Поначалу это немного сбивает с толку, но причина этого в том, что нам нужно добавить объект this в myFunction — Javascript в строгом режиме не будет по умолчанию использовать его как глобальный объект. Для этого мы должны использовать call(). В приведенном ниже примере я превратил myObject в нашу переменную this:


```javascript


"использовать строгий"


console.log(этот); // То же, что и console.log(window);


пусть мойОбъект = {


Имя: "Джон",


фамилия: "Доу",


возраст: 76 лет


функция моя функция () {


console.log(это.имя);


моя функция. вызов (мой объект); // this.firstName определено как "John", поэтому он будет вести консольный журнал John


мояФункция(); // this.firstName будет неопределенным, и это вызовет ошибку.


call() запускает myFunction и прикрепляет myObject к ключевому слову this. Если мы не будем использовать вызов, а просто запустим myFunction(), то функция вернет ошибку, так как this.firstName будет неопределенным. Вы также можете вызвать функцию с пустым this, к которому затем можно добавить данные внутри вашей функции.


Это дает нам новое пространство для определения переменных в нашем объекте this, а не засорении данными из глобального объекта this:


```javascript


"использовать строгий"


console.log(этот); // То же, что и console.log(window);


функция моя функция () {


this.firstName = 'Джон';


console.log(это.имя); // Это будет "Джон"


мояФункция.вызов({});


Другое поведение в строгом режиме


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


Позвоните и подайте заявку


Иногда вы можете видеть, что call() используется взаимозаменяемо с функцией, называемой apply(). Обе эти функции очень похожи в том смысле, что обе они вызывают функцию с указанным контекстом this. Единственная разница в том, что apply() принимает массив, если у функции есть аргументы, а call() принимает каждый аргумент один за другим.


Например:


```javascript


"использовать строгий"


пусть другие числа = {


а: 10,


б: 4


функцияmultiNumbers(x, y, z) {


вернуть this.a * this.b * x * y * z


// Оба вернут один и тот же результат, единственная разница


// в том, что apply() использует массив для аргументов.


умножитьЧисла.вызов(другиеЧисла, 1, 2, 3);


умножитьЧисла.применить(другиеЧисла, [ 1, 2, 3 ]);


Упрощение этого процесса с помощью bind()


Другой способ добиться поведения, аналогичного call(), это использовать bind(). Подобно call(), bind() изменяет значение this для функции, только делает это постоянно. Это означает, что вам не нужно постоянно использовать bind() — вы используете его только один раз.


Вот пример, где мы постоянно привязываем наш объект к нашей функции, тем самым постоянно обновляя его — нам просто нужно определить его как новую функцию. В приведенном ниже примере мы определяем новую функцию с именемboundFunction, которая является нашей функцией myFunction с постоянно привязанным к ней myObject.


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


```javascript


"использовать строгий"


console.log(этот); // То же, что и console.log(window);


пусть мойОбъект = {


Имя: "Джон",


фамилия: "Доу",


возраст: 76 лет


функция моя функция () {


console.log(это.имя);


пустьboundFunction = myFunction.bind(myObject); // это навсегда привяжет this к myObject.


связанная функция(); // так как мы использовали привязку, теперь для этого будет установлено значение myObject каждый раз, когда мы вызываемboundFunction(), поэтому она будет возвращать John.


Функции обозначения стрелок и это


Одной из ключевых особенностей функций нотации стрелок в Javascript является то, что они не содержат этот контекст. Это означает, что они наследуют это от своего родителя. Например, предположим, что мы находимся в строгом режиме и определяем как функцию стрелки, так и функцию «нормального» стиля. Для функции стрелки это будет унаследовано, но для другой функции это останется неопределенным!


```javascript


"использовать строгий"


console.log(этот); // То же, что и console.log(window);


функция моя функция () {


console.log(это.имя); // Это будет "Джон"


пусть myArrowFunction = () => {


console.log(это.имя); // Это будет "Джон"


пусть myNormalFunction = функция () {


console.log(это.имя); // Это вызовет ошибку, так как это не определено!


мояФункцияСтрелки();


моя нормальная функция();


myFunction.call({


имя: "Джон"


Функции-конструкторы и this


Еще одна интересная вещь заключается в том, что при использовании в функции-конструкторе (которая является функцией, использующей ключевое слово new), возврат функции-конструктора существенно перезаписывает this. Так, например, если мы запустим следующее, хотя мы установим this.name на John, возвращаемое значение для name будет Jack:


```javascript


пусть функцияA = функция () {


this.name = "Джон";


пусть функцияB = функция () {


this.name = "Джон";


вернуть {


имя: "Джек"


пусть runFunctionA = новая функцияA();


console.log(runFunctionA.name); // Возвращает "Джон";


пусть runFunctionB = новая функцияB();


console.log(runFunctionB.name); // Возвращает "Джек";


Это в контексте объекта


В контексте объекта использование this относится к объекту. Например, предположим, что мы запускаем функцию в объекте с именем obj, который ссылается на this.aProperty — в данном случае это ссылается на obj:


```javascript


пусть объект = {


aНедвижимость: 15,


функция выполнения: функция () {


console.log(это.aProperty); // Относится к 15


obj.runFunction(); // Выведет консольный лог 15, так как это относится к obj


Это также верно, если вы используете нотацию get()/set():


```javascript


"использовать строгий"


пусть объект = {


aНедвижимость: 15,


функция выполнения: функция () {


console.log(это.aProperty); // Относится к 15


установить updateProp (дивизия) {


this.aProperty = this.aProperty / подразделение; // this.aProperty ссылается на 15


console.log(это.aProperty);


obj.updateprop = 15; // Разделим aProperty на 15 и запишем в консоль результат, т. е. 1


Использование этого с прослушивателями событий


Еще одна особенность Javascript заключается в том, что при использовании прослушивателя событий this относится к HTML-элементу, к которому было добавлено событие. В приведенном ниже примере мы добавляем событие клика в HTML-тег с идентификатором «hello-world»:


document.getElementById('hello-world').addEventListener('щелчок', function(e) {


console.log(этот);


Если мы затем нажмем на наш HTML-элемент #hello-world, мы увидим это в нашем журнале консоли:


```html



Использование этого с классами


В этом разделе стоит отметить, что классы в Javascript — это просто функции внутри. Это означает, что многие функциональные возможности, которые мы видели в функциях, применимы и к классам.


По умолчанию класс будет иметь этот набор для самого экземпляра класса. В приведенном ниже примере мы можем увидеть это в действии — как runClass.name, так и runClass.whatsMyName возвращают John.


```javascript


класс мой класс {


Какое мое имя() {


вернуть это.имя;


получить имя () {


вернуть «Джон»;


const runClass = новый myClass();


console.log(runClass.name); // Возвращает "Джон"


console.log(runClass.whatsMyName); // Возвращает "Джон"


Единственным исключением является то, что сюда не добавляются статические элементы. Итак, если мы определим функцию с ключевым словом static перед ней, это не будет на этом:


```javascript


класс мой класс {


получить мой возраст () {


вернуть this.whatsMyAge();


статический whatsMyAge () {


вернуть это.возраст;


получить имя () {


вернуть «Джон»;


получить возраст () {


вернуться 143


const runClass = новый myClass();


console.log(runClass.whatsMyAge()); // Выдает ошибку, так как runClass.whatsMyAge() не определен


console.log(runClass.getMyAge()); // Выдает ошибку, так как this.whatsMyAge() не определен


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


Заключение


В Javascript это может означать разные вещи. В этой статье мы рассмотрели, что это означает в разных контекстах — функциях, классах и объектах. Мы рассмотрели, как использовать bind(), call() и apply(), чтобы добавить другой контекст к вашим функциям.


Мы также рассмотрели, как использовать это в строгом режиме по сравнению с нестрогим режимом. После этого, я надеюсь, «это» немного демистифицируется.


  • Впервые опубликовано [здесь] (https://fjolt.com/article/javascript-this-and-the-global-object)*


Оригинал
PREVIOUS ARTICLE
NEXT ARTICLE