Упрощение ключевого слова JS со слишком большим багажом

«Это» — слово, которое мы все хорошо знаем. По определению — «ссылаясь на конкретную вещь или ситуацию, только что упомянутую» (спасибо Google!). Однако, когда мы впервые изучаем «это» в Javascript, оно сразу же становится словом, запутанным и невероятно глубоким. Для меня тоже «это» далось нелегко, и я не чувствую, что мое путешествие на этом закончилось. Тем не менее, каждый раз, когда я чувствую себя сбитым с толку, я думаю об этом же определении Google и вспоминаю, что оно означает почти то же самое и в JS.

"this" относится к объекту, для которого вызывается функция.

Конечно, это не так просто. Чтобы понять «это», мы должны понять контекст.

Что такое контекст?

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

console.log(this)
// Window {window: Window, self: Window, document: document ...
window.sayHi = 'Hello'
console.log(this.sayHi) // 'Hello'

В глобальной области это каждый раз относится к объекту окна (если только вы не находитесь в строгом режиме, где оно будет неопределенным, но пока не беспокойтесь об этом). Как видно из первого примера, console.log со значением this возвращает объект окна. Во втором мы видим, как мы можем добавить свойство к окну и ссылаться на него позже, используя this. Обычно такое использование этого не рекомендуется, поскольку изменение самого объекта глобального окна может иметь много нежелательных побочных эффектов.

Контекст вызова функции

function sayHello(){
   const greeting = 'Hola!'
  
   console.log(this.greeting) //undefined
   console.log(this) //global object (Window)
}
sayHello();

При обычном вызове функции, когда функция не является свойством объекта (как показано выше), 'this' будет по-прежнему ссылаться на окно, так как он еще не видел объекта, на который можно было бы ссылаться. . В первом console.log this.greeting возвращает неопределенное значение, так как приветствие не является свойством глобального объекта окна. Однако второй console.log выведет объект окна на консоль.

Функция была вызвана для глобального объекта, поэтому будет ссылаться на этот объект.

Контекст вызова метода

Помните, я уже говорил, что функция не является свойством объекта? Как вы думаете, что происходит, когда это происходит?

const friend = {
  name: 'Jamar',
  greet: function() {
      console.log('Hi! My name is ' + this.name);
  }
};

friend.greet(); //'Hi! My name is Jamar

И снова у нас есть функция, использующая this. Чем это отличается? Спросите себя — где мы вызываем функцию? В этом случае функция является частью пары ключ: значение, где имя ключа — приветствие. Когда мы вызываем приветствие, мы вызываем его для переменной friend, которая является объектом. Объект. При работе с функциями как объектными методами посмотрите слева от точки, и это будет то, на что ссылается «это». (Примечание: «это» не будет работать так же, если используется во вложенных функциях внутри метода объекта)

Функции конструктора

Теперь мы подходим к менее общим ситуациям. Функции-конструкторы — это шаблоны для создания объектов и, следовательно, функции, которые всегда связаны с объектом.

function FavFood(name, food) {
  this.name=name;
  this.food=food;
  this.intro= function () {
     return `Hi, my name is ${this.name} and I like ${this.food}`;
  }
};
const sam = new FavFood('Sam', 'pizza');
console.log(sam.name) // 'Sam'
console.log(sam.food) // 'pizza'
console.log(sam.intro()) //'Hi my name is Sam and I like pizza'

В этой функции-конструкторе мы используем this для создания свойств для каждого объекта при каждом его вызове. Способ, которым JS может определить, на какой объект ссылается «это», — это ключевое слово «новое». Когда мы используем «новый», мы говорим JS создать новый объект и использовать нашу функцию-конструктор для создания его свойств. В функциях-конструкторах this всегда будет относиться к объекту, создаваемому функцией.

Функции со стрелками

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

const myObject = {
  name: 'Jack',
  food: 'Popeyes',
  sayHi: () => {
    console.log(this)
  }
};
myObject.sayHi() //Window object

Как видно выше, this наследует свою область действия от своего родителя, myObject, и будет указывать на объект Window. Если бы он был написан нормально, он бы не наследовал область видимости от своего родителя и не указывал бы на myObject.

const myObject = {
  name: 'Jack',
  food: 'Popeyes',
  sayHi: function() {
    console.log(this)
  }
};
myObject.sayHi() //{name: 'Jack', food: 'Popeyes', sayHi: ƒ}

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

Когда стрелочные функции выполняют свою работу

const friend = {
  name: 'Jamar',
  greet: function() {
     console.log('Hi! My name is ' + this.name);
     const innerFunc = () => console.log(this.name)
     innerFunc()
  }
};

friend.greet(); 
// 'Hi! My name is Jamar'
// 'Jamar'

Здесь innerFunc наследует область действия от своего родителя, функции, на которую ссылается friend.greet(). this указывает на объект, на который указывает переменная friend. Я склонен думать, что «это» в стрелочных функциях относится к двум областям действия, отстоящим от того места, где оно написано. В одной области видимости будет функция приветствия, а в двух — дружественный объект. Без стрелочных функций второй console.log вернул бы значение undefined.

const friend = {
  name: 'Jamar',
  greet: function() {
     console.log('Hi! My name is ' + this.name);
     let innerFunc = function(){ console.log(this.name)}
     innerFunc()
  }
};

friend.greet(); 
// 'Hi! My name is Jamar'
// undefined

Заключение // TL;DR

В общем, это сложное ключевое слово, используемое для ссылки на объект, для которого вызывается функция. У него много правил и особенностей, но всегда важно спрашивать себя, на какой объект «это» указывает. Вот основные выводы —

  • this относится к объекту, для которого вызывается функция
  • ссылается на глобальный объект окна при обычном вызове функции
  • ссылается на объект, свойством которого он является (СЛЕВА ОТ ТОЧКИ!) при вызове метода
  • относится к новому создаваемому объекту в функциях-конструкторах
  • в стрелочных функциях this наследует свою область действия от своего родителя. Подумайте об «этом», относящемся к области двумя слоями выше, где это написано. Это может быть особенно полезно во вложенных функциях.