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

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

let myObject = {
  name: "Peter",
  age: 20,
  aboutMe: function(){
    return "My name is "+this.name+", I am "+this.age + " years old"
  }
}
console.log(myObject.aboutMe());

Как вы могли заметить, код выведет в консоль следующее:

My name is Peter, I am 20 years old

Мы использовали this для ссылки на объект, который функция содержится в (myObject). Выглядит довольно просто, не так ли? Теперь обратите внимание на это

let myObject2 = {
  name: "Prisca",
  age: 24,
  aboutMe: function(){
    return "My name is "+this.name+", I am "+this.age + " years old"
  },
  child: {
    name: "Sonia",
    age: 2,
    aboutMe: function(){
      return "My name is "+this.name+", I am "+this.age + " years old"
    }
  }
};
console.log(myObject2.aboutMe());
console.log(myObject2.child.aboutMe());

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

Глобальный объект

Любая привязка или функция, не определенные внутри объекта, прикрепляются к глобальному объекту, который является объектом Window в браузере. Вы можете проверить это, набрав this; в консоли браузера и нажав клавишу ВВОД. Это вернет объект Window со свойствами и значениями, как вы ожидаете от обычного объекта. Вы даже можете изменить свойства объекта Window. Вставьте следующее в консоль

console.log(this.color); //Undefined
this.color = "Green";
console.log(this.color); //Green

Первая строка возвращает undefined, потому что объект Window не имеет свойства с именем color. Вторая строка создает свойство color и присваивает ему значение "Green". И вы можете видеть, что теперь к этому свойству можно получить доступ в третьем операторе. Важно отметить, что объект Window различается на разных вкладках браузера, поэтому вновь созданное свойство не будет существовать за пределами этой вкладки.

Явное «это» заявление

До сих пор мы полагались на среду выполнения JavaScript, чтобы помочь нам определить, каким должно быть значение this. Хотя в некоторых случаях это нормально, в других случаях это дает нежелательные результаты. Возьмем, к примеру, эту асинхронную функцию. Как вы думаете, каким будет результат? Попробуйте это в консоли.

let myObject3 = {
  firstName: "Michael",
  age: 20,
  aboutMe: function(){
    setTimeout(function(){
      console.log(this.firstName + " is " + this.age + " years old")
    },5000)
  }
}
myObject3.aboutMe();

Метод aboutMe имеет функцию, которая выполняется только через 5 секунд, поэтому при вызове она «сохраняет» функцию на будущее. Когда он в конечном итоге выполняется, он делает это в глобальной области и, как таковой, возвращает

undefined is undefined years old.

Причина, по которой это произошло, заключается просто в том, что ключевое слово this во время выполнения относится к объекту Window, а не к myObject3, и поскольку ни одно из этих свойств не существует в объекте Window, оно возвращает undefined. Так как же решить эту проблему? Мы должны каким-то образом сообщить функции Async, что такое ключевое слово this в любой момент выполнения. JavaScript предоставляет 3 способа сделать это явно: call, apply и bind.

Метод вызова

Точно так же, как объекты имеют предопределенные свойства (прототипы), такие как toString, функции также имеют предопределенные методы. Один из таких методов - call. Метод call имеет множество приложений и вариантов использования, но в конечном итоге решает одну проблему, которая заключается в установке значения this в функции, для которой он вызывается.

Возьмем, к примеру, два отдельных объекта, похожих на те, которые мы использовали раньше.

let user1 = {
  name: "Tony",
  sayHi: function(){
    return this.name + " says Hi"
  }
}
let user2 = {
  name: "Martha",
  sayHi: function(){
    return this.name + " says Hi"
  }
}
console.log(user1.sayHi()); //Tony says Hi
console.log(user2.sayHi()); //Martha says Hi

Оба объекта имеют одну и ту же функцию sayHi, что является ненужным дублированием кода. Мы можем написать эту функцию один раз и использовать ее во втором объекте, используя метод call функции для определения значения this.

let user1 = {
  name: "Tony",
  sayHi: function(){
    return this.name + " says Hi"
  }
}
let user2 = {
  name: "Martha"
}
console.log(user1.sayHi());           //Tony says Hi
console.log(user1.sayHi.call(user2)); //Martha says Hi

Здесь мы использовали функцию user1 sayHi и call метод, чтобы определить значение this, передав аргумент user2. Если у функции были параметры, мы можем включить аргументы в метод call

let user1 = {
  name: "Tony",
  greet: function(msg, friend){
    return this.name + " says " +msg+ " to "+friend
  }
}
let user2 = {
  name: "Martha"
}
console.log(user1.greet("Hi", "James"));
//Tony says Hi to James
console.log(user1.greet.call(user2, "Hello", "Habeeb"));
//Martha says Hello to Habeeb

Метод Apply

Подобно методу call, метод apply делает то же самое, за одним исключением, он принимает только один аргумент. Если функция имеет более одного параметра, мы должны передать аргументы в виде массива.

console.log(user1.greet.apply(user2, ["Hello", "Habeeb"]));

И call, и apply немедленно выполняют функцию, к которой они обращаются. Это создает еще одну проблему, когда мы хотим использовать их в вызове асинхронной функции, как функция setTimeout, которую мы использовали раньше.

Метод привязки

В отличие от call и apply, bind возвращает функцию, для которой он был вызван, но не выполняет ее сразу.

let myObject4 = {
  firstName: "Michael",
  age: 20,
  aboutMe: function(){
    setTimeout(function(){
      console.log(this.firstName + " is " + this.age + " years old")
    }.bind(myObject4),5000)
  }
}
myObject4.aboutMe(); //Michael is 20 years old

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

До сих пор мы видели 3 способа явной установки значения this. Есть четвертый способ сделать то же самое - ключевое слово new, но он выходит за рамки данной статьи.