это не тайна, это просто правила.
давайте углубимся в ключевое слово 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, которое является важной концепцией, особенно при работе с объектными методами и функциями.
Подводя итог, вот несколько ключевых моментов:
- В обычной функции (не стрелочной)
this
обычно относится к объекту, методом которого является функция. Однако, если обычная функция не является методом объекта,this
ссылается на глобальный объект (в браузере этоwindow
) - Стрелочные функции не имеют собственного
this
. Они наследуютthis
от родительской области, которая определяется при определении функции, а не при ее вызове. - В обработчиках событий
this
относится к элементу, вызвавшему событие. мы не рассматривали это здесь. - Значение
this
можно явно установить с помощью таких методов, какcall()
,apply()
иbind()
. ( обычно я не использую call() apply() bind() )
Помните, что поведение this
в JavaScript иногда может быть нелогичным, особенно когда речь идет о стрелочных функциях и обычных функциях. Всегда полезно тщательно протестировать свой код и убедиться, что this
относится к тому, чего вы от него ожидаете.
Если вы понимаете эти принципы, вы будете хорошо подготовлены к решению большинства ситуаций, связанных с this
в JavaScript.