Понимание магии «этого» в 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)*
Оригинал