это в глобальном контексте:

Если мы запустим этот оператор в консоли браузера, он вернет true.

this === window //true

"use strict"
this === window //true

По сути, в браузере это ключевое слово, если оно не используется внутри какого-либо контекста выполнения (функции), будет относиться к окну, поскольку оно является глобальным объектом.

А как насчет среды выполнения nodejs? Он по-прежнему будет ссылаться на глобальный объект, но в узле глобальный объект буквально называется глобальным.

this === global //true

Однако это верно только в узле REPL (цикл чтения-оценки-печати). Давайте выполним ту же строку кода в модуле узла. Внезапно мы ошибаемся. Это связано с тем, что в коде верхнего уровня модуля узла это эквивалентно module.exports.

// Custom Node module
console.log(this === global) //false      
console.log(this === module.exports) //true

Механизм узла запускает код каждого модуля внутри функции REPL. Эта функция REPL вызывается со значением this, равным module.exports.

это в вызове функции:

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

function func() {
  console.log(this === global); //true
}

func();

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

"use strict";

function func() {
  console.log(this === undefined); //true
}

func();

Давайте теперь посмотрим, почему это значение имеет смысл быть неопределенным в строгом режиме. Вот простая функция человека, которая принимает два параметра и инициализирует два свойства. Обратите внимание, что этот фрагмент кода не находится в строгом режиме. Если мы вызовем функцию человека с помощью простого вызова функции, результат, вероятно, будет не таким, как мы предполагали. Переменная person имеет значение undefined.

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

function Person(firstName, lastName) {
  this.firstName = firstName;
  this.lastName = lastName;
}

const person = Person("Jason", "Tom");
console.log(person); //undefined
console.log(global.firstName); //Jason
console.log(global.lastName); //Tom

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

const person = new Person("Jason", "Tom");
console.log(person); //{firstName: Jason, lastName: Tom}
console.log(global.firstName); //undefined
console.log(global.lastName); //undefined

Теперь мы правильно создаем нового человека, а также больше не засоряем глобальный объект свойствами имени и фамилии.

это в вызове конструктора:

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

  1. Новый пустой объект создается и назначается this.
  2. Выполняется тело функции. Обычно он модифицирует this, добавляет к нему новые свойства.
  3. Возвращается значение this.
function Person(firstName, lastName) {
  // this = {};  (implicitly)

  // add properties to this
  this.firstName = firstName;
  this.lastName = lastName;

  // return this;  (implicitly)
}

const person = new Person("Jason", "Tom"); //{firstName: Jason, lastName: Tom}

Методы в конструкторе:

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

function Person(name) {
  this.name = name;

  this.sayHi = function() {
    alert( "My name is: " + this.name );
  };
}

let adam= new Person("Adam");

adam.sayHi(); // My name is: Adam

это в вызове метода:

Когда функция вызывается как метод объекта, аргумент this этой функции устанавливается равным объекту, для которого вызывается метод. Здесь мы вызываем person.sayHi, поэтому это значение в методе sayHi относится к человеку.

const person = {
  firstName: "John",
  sayHi() {
      console.log(`Hi, my name is ${this.firstName}!`);
  }
};

person.sayHi(); //"Hi, my name is John!"

Мы говорим, что этот человек является получателем вызова метода. Этот механизм приемника не зависит от того, где была определена функция. Например, мы могли бы определить функцию отдельно, а потом привязать ее к человеку. Мы по-прежнему пишем person.sayHi, поэтому получателем вызова метода по-прежнему будет человек.

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

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

let person = {
  firstName: "John",
  sayHi() {
    alert(`Hello, ${this.firstName}!`);
  }
};

setTimeout(user.sayHi, 1000); // Hello, undefined!

Одним из решений этой проблемы является добавление функции-оболочки. Таким образом, person.sayHi по-прежнему вызывается как метод и не теряет предполагаемого получателя. Другим решением является метод bind, который позволяет нам привязать this к конкретному объекту.

Жестко привязать это значение функции с помощью метода .bind()

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

Мы можем решить эту проблему, используя метод привязки. Bind создаст новую функцию sayHi и навсегда установит в качестве этого значения человека. Этот механизм иногда называют жестким связыванием. Затем мы можем передать функцию жесткой привязки, чтобы установить тайм-аут.

const person = {
  firstName: "John",
  sayHi() {
      console.log(`Hi, my name is ${this.firstName}!`);
  }
};

setTimeout(person.sayHi, 1000); //"Hi, my name is undefined!"
setTimeout(person.sayHi.bind(person), 1000); //"Hi, my name is John!"

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

Что возвращает привязка? Ну, он возвращает другую функцию. Наконец, bind позволяет нам заблаговременно исправить ряд аргументов, когда мы связываем исходную функцию. Когда наша новая функция вызывается, два списка аргументов объединяются. Здесь мы фактически делаем частичное применение.

Укажите это с помощью .call() или .apply()

Как бы мы вызвали sayHi с человеком в качестве получателя? Мы можем сделать это с помощью метода call. Вызов определен в прототипе функции и, следовательно, доступен для каждой функции.

function sayHi(lastname) {
  console.log(`Hi, my name is ${this.firstName}, ${lastname}!`);
}

const person = {
  firstName: "Jane"
};

sayHi.call(person, 'Adams'); // "Hi, my name is Jane, Adams!"

Как мы видим, это в этом методе sayHi относится к значению, которое мы предоставляем явно, у нас есть первый аргумент. Этот аргумент часто называют this arg. В качестве альтернативы мы можем использовать метод apply, который также определен в прототипе функции.

function sayHi(lastname) {
  console.log(`Hi, my name is ${this.firstName}, ${lastname}!`);
}

const person = {
  firstName: "Jane"
};

sayHi.apply(person, ['Adams']); // "Hi, my name is Jane, Adams!"

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

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

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

это с функцией стрелки:

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

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

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

Прозрачная привязка «this» стрелочных функций особенно полезна, когда мы хотим получить доступ к «this» в обратном вызове. Рассмотрим этот объект Counter. Допустим, мы хотим увеличивать свойство count на единицу каждую секунду. Мы могли бы использовать setInterval() и обеспечить обратный вызов, который увеличивает this.count.

const counter = {
  count: 0,
  incrementPeriodically() {
      setInterval(function () {
          console.log(++this.count); //null null null
      }, 1000);
  }
};

counter.incrementPeriodically();

Однако, если мы запустим эту программу, мы увидим, что она не работает. Привязка «this» в нашем функциональном выражении не относится к нашему объекту Counter. Он ссылается на глобальный объект, потому что именно так работает setInterval(). Мы пытаемся увеличить свойство count глобального объекта.

Преобразуем наше функциональное выражение в стрелочную функцию. Теперь наш обратный вызов использует привязку «this» из метода периодического приращения. Поскольку мы периодически вызывали increment, используя синтаксис метода, для this установлено значение counter. Все работает.

const counter = {
  count: 0,
  incrementPeriodically() {
      setInterval(() => {
          console.log(++this.count); // 1 2 3 4 5
      }, 1000);
  }
};

counter.incrementPeriodically();