это не тайна, это просто правила.

давайте углубимся в ключевое слово this в JavaScript

Уровень 1 — базовое понимание

Проще говоря, this — это специальное ключевое слово в JavaScript, которое относится к контексту, в котором вызывается функция. Другими словами, это относится к объекту, для которого функция является методом. В этом случае this внутри sayHello относится к obj, потому что sayHello является методом obj.

let obj = {
  name: 'John',
  sayHello: function() {
    console.log('Hello, ' + this.name);
  }
};

obj.sayHello();  // Outputs: 'Hello, John'

Уровень 2 — Понимание this в различных контекстах

Значение this может меняться в зависимости от контекста, в котором вызывается функция. Вот четыре основных правила связывания this.

Привязка по умолчанию: если функция вызывается как отдельная функция (не как метод, конструктор или функция стрелки), this будет ссылаться на глобальный объект (window в браузере, global в Node. js). Это относится к режиму использовать строгий, где this будет undefined.

function sayHello() {
  console.log(this);  
}
sayHello();  // Outputs: Window {...} (or global object in Node.js)

Неявное связывание: если функция вызывается как метод объекта, this относится к объекту.

let obj = {
  name: 'John',
  sayHello: function() {
    console.log(this.name);
  }
};
obj.sayHello();  // Outputs: 'John'

Явная привязка: мы можем явно указать, к чему относится this, используя call, apply или bind. (я не так часто этим пользуюсь)

let obj1 = {
  name: 'John'
};

let obj2 = {
  name: 'Jane'
};

function sayHello() {
  console.log(this.name);
}

sayHello.call(obj1);  // Outputs: 'John'
sayHello.call(obj2);  // Outputs: 'Jane'

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

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

let john = new Person('John');
console.log(john.name);  // Outputs: 'John'

Уровень 3 — Расширенные концепции и подводные камни. Теперь, когда у вас есть четкое представление об основах, давайте углубимся в некоторые дополнительные темы и возможные подводные камни.

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

let obj = {
  name: 'John',
  sayHello: function() {
    setTimeout(() => {
      console.log(this.name);  // 'this' refers to 'obj'
    }, 1000);
  }
};
obj.sayHello();  // Outputs: 'John' after 1 second // this is lexically bound to the object john

Подводный камень — потеря контекста this. Распространенной ошибкой является потеря контекста this в обратных вызовах или обработчиках событий.

let obj = {
  name: 'John',
  sayHello: function() {
    setTimeout(function() {
      console.log(this.name);  // 'this' does not refer to 'obj'
    }, 1000);
  }
};

obj.sayHello();  // Outputs: undefined (or an error in strict mode)

this внутри обратного вызова setTimeout не ссылается на объект obj, как можно было бы ожидать. Вместо этого он ссылается на глобальный объект (window в среде браузера, global в Node.js), и, поскольку в глобальном объекте нет свойства name, выводится undefined.

Когда вы используете функцию в качестве обратного вызова, как в setTimeout, функция не используется как метод объекта, а вместо этого вызывается как отдельная функция. Вот почему this внутри обратного вызова setTimeout относится к глобальному объекту. (важно отметить, что в setTimeOut() функция обратного вызова определяется с помощью функции ключевого слова.

В JavaScript при создании функции с ключевым словом function создается новый контекст для this. Функции, объявленные с помощью ключевого слова function, ведут себя следующим образом: когда функция вызывается, значение this определяется тем, как вызывается функция, а не где. strong> функция определена. Это называется «контекстом выполнения» или «контекстом вызова» функции. (обратите внимание, что это объяснение справедливо для функций, определенных с помощью ключевого слова function.)

Когда я использую ключевое слово function для объявления функции, значение this определяется тем, как вызывается функция:

  • Если функция вызывается как метод объекта, this устанавливается на объект, для которого вызывается метод. Например, в obj.method() this внутри method относится к obj.
  • Если функция вызывается как отдельная функция, а не как метод объекта, this устанавливается на глобальный объект (window в браузере, global в Node.js). Например, в функции func(), которая не является методом объекта, this внутри func относится к глобальному объекту.
  • Если функция вызывается как конструктор (с ключевым словом new), this устанавливается для вновь созданного объекта.
  • Если функция вызывается с использованием call, apply или bind, this устанавливается для объекта, который передается в качестве первого аргумента для call, apply или bind.

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

Функции стрелки и разрешение?

С другой стороны, стрелочные функции не создают своего собственного this контекста, поэтому this внутри стрелочной функции лексически или статически связаны. Это означает, что для него установлено значение this внешней функции, в которой определена функция стрелки. Обратите внимание, что внешняя функция по-прежнему определяется ключевым словом function.

let obj = {
  name: 'John',
  sayHello: function() {
    setTimeout(() => {
      console.log(this.name);  // 'this' refers to 'obj' because arrow functions use the 'this' of the enclosing context
    }, 1000);
  }
};
obj.sayHello();  // Outputs: 'John'

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

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

let obj1 = {
  name: 'John',
  sayHello: () => {
    console.log(this.name);
  }
};

obj1.sayHello();  // Outputs: undefined

позвольте проиллюстрировать это еще подробнее,

function outerFunction() {
  let obj1 = {
    name: 'John',
    sayHello: () => {
      console.log(this.name);  
    }
  };

  obj1.sayHello();  // Outputs: undefined
}

outerFunction();

В этом случае obj1 определяется внутри области действия outerFunction. Однако, поскольку sayHello является стрелочной функцией, this внутри sayHello не относится к obj1. Вместо этого this относится к контексту outerFunction.

Функция outerFunction не является методом объекта, это обычная функция. Следовательно, значение this внутри outerFunction (и, следовательно, внутри sayHello) определяется тем, как вызывается outerFunction. В этом случае outerFunction вызывается как отдельная функция, поэтому this внутри outerFunction относится к глобальному объекту.

Таким образом, this.name внутри sayHello пытается получить доступ к свойству name глобального объекта, то есть undefined, и поэтому obj1.sayHello() выводит undefined.

давайте углубимся и вуаля!

let obj2 = {
  name: 'Jane',
  outerFunction: function() {
    let obj1 = {
      name: 'John',
      sayHello: () => {
        console.log(this.name);  
      }
    };

    obj1.sayHello();  // Outputs: 'Jane'
  }
};

obj2.outerFunction();

В этом случае outerFunction является методом obj2, поэтому, когда outerFunction вызывается как obj2.outerFunction(), значение this внутри outerFunction равно obj2.

Поскольку sayHello является стрелочной функцией, у нее нет собственного this, она наследует this от своей родительской области, то есть outerFunction. Следовательно, this внутри sayHello относится к obj2, а не obj1.

Вот почему this.name внутри sayHello относится к свойству name obj2, а не к свойству name obj1, а obj1.sayHello() выводит «Джейн», а не «Джон».

Сводка

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

Подводя итог, вот несколько ключевых моментов:

  1. В обычной функции (не стрелочной) this обычно относится к объекту, методом которого является функция. Однако, если обычная функция не является методом объекта, this ссылается на глобальный объект (в браузере это window)
  2. Стрелочные функции не имеют собственного this. Они наследуют this от родительской области, которая определяется при определении функции, а не при ее вызове.
  3. В обработчиках событий this относится к элементу, вызвавшему событие. мы не рассматривали это здесь.
  4. Значение this можно явно установить с помощью таких методов, как call(), apply() и bind(). ( обычно я не использую call() apply() bind() )

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

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