Сегодняшний урок любезно предоставлен талантливым Просветление JavaScript» . Он обсуждает путаницу this
ключевого слова и различные способы определения и установки его значения.
Каждые несколько недель мы пересматриваем некоторые из любимых постов нашего читателя на протяжении всей истории сайта. Этот учебник был впервые опубликован в июле 2011 года.
Концептуальный обзор this
Когда создается функция, создается ключевое слово с именем this (за кулисами), которое ссылается на объект, в котором работает функция. Иными словами, это доступно для области его функции, но является ссылкой на объект, свойство которого является функцией / методом.
Давайте посмотрим на этот объект:
01
02
03
04
05
06
07
08
09
10
11
|
<!DOCTYPE html><html lang=»en»><body><script>
var cody = {
living:true,
age:23,
gender:’male’,
getGender:function(){return cody.gender;}
};
console.log(cody.getGender());
</script></body></html>
|
Обратите внимание, что внутри функции getGender
мы получаем доступ к свойству пола с помощью точечной нотации (например, cody.gender
) на самом объекте cody. Это можно переписать, используя this
для доступа к объекту cody
потому что this
указывает на объект cody
.
01
02
03
04
05
06
07
08
09
10
11
|
<!DOCTYPE html><html lang=»en»><body><script>
var cody = {
living:true,
age:23,
gender:’male’,
getGender:function(){return this.gender;}
};
console.log(cody.getGender());
</script></body></html>
|
this.gender
используемый в this.gender
просто ссылается на объект cody
для которого
эксплуатации.
Тема this
может сбивать с толку, но это не должно быть. Просто помните, что, как правило, this
используется внутри функций для ссылки на объект, в котором содержится функция, в отличие от самой функции (кроме исключений использование new
ключевого слова или call()
и apply()
).
Важные заметки
- Ключевое слово
this
выглядит и действует как любая другая переменная, за исключением того, что вы не можете изменить ее. - — В отличие от
arguments
и любых параметров, отправляемых функции,this
ключевое слово (не свойство) в объекте вызова / активации.
Как определяется ценность this
?
Значение this
, переданное всем функциям, основано на контексте, в котором функция вызывается во время выполнения. Обратите внимание, потому что это одна из тех причуд, которые вам просто нужно запомнить.
Объекту myObject
в приведенном ниже коде присваивается свойство sayFoo
, которое указывает на функцию sayFoo
. Когда функция sayFoo
вызывается из глобальной области видимости, это относится к объекту окна. Когда он вызывается как метод myObject
, this
относится к myObject
.
My Поскольку myObject
имеет свойство с именем foo
, это свойство используется.
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
|
<!DOCTYPE html><html lang=»en»><body><script>
var foo = ‘foo’;
var myObject = {foo: ‘I am myObject.foo’};
var sayFoo = function() {
console.log(this[‘foo’]);
};
// give myObject a sayFoo property and have it point to sayFoo function
myObject.sayFoo = sayFoo;
myObject.sayFoo();
sayFoo();
</script></body></html>
|
Ясно, что значение this
зависит от контекста, в котором вызывается функция. Учтите, что и myObject.sayFoo
и sayFoo
указывают на одну и ту же функцию. Однако, в зависимости от того, откуда (то есть, от контекста) sayFoo()
, значение this
может быть другим.
Если это помогает, здесь тот же код с явно используемым объектом head (т.е. window
).
01
02
03
04
05
06
07
08
09
10
|
<!DOCTYPE html><html lang=»en»><body><script>
window.foo = ‘foo’;
window.myObject = {foo: ‘I am myObject.foo’};
window.sayFoo = function() { !
window.myObject.sayFoo = window.sayFoo;
window.myObject.sayFoo();
window.sayFoo();
</script></body></html>
|
Убедитесь, что, передавая функции или имея несколько ссылок на функцию, вы понимаете, что значение this
будет меняться в зависимости от контекста, в котором вы вызываете функцию.
Важная заметка
- Все переменные, кроме
this
и arguments, следуют лексической области видимости .
Это ключевое слово относится к объекту головы во вложенных функциях.
Вам может быть интересно, что происходит с this
когда он используется внутри функции, которая содержится внутри другой функции. Плохая новость в ECMA 3, this
заблудилась и относится к головному объекту (объект window
в браузерах), а не к объекту, внутри которого определена функция.

В приведенном ниже коде this
внутри func2
и func3
теряет свой путь и ссылается не на myObject
а вместо этого на объект head.
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
|
<!DOCTYPE html><html lang=»en»><body><script>
var myObject = {
func1:function() {
console.log(this);
varfunc2=function() {
console.log(this);
varfunc3=function() {
console.log(this);
}();
}();
}
};
myObject.func1();
</script></body></html>
|
Хорошая новость заключается в том, что это будет исправлено в ECMAScript 5. На данный момент вы должны знать об этом затруднительном положении, особенно когда вы начинаете передавать функции в качестве значений другим функциям.
Рассмотрим приведенный ниже код и то, что происходит при передаче анонимной функции в foo.func1. Когда анонимная функция вызывается внутри foo.func1
(функция внутри функции), this
значение внутри анонимной функции будет ссылкой на объект head.
01
02
03
04
05
06
07
08
09
10
|
<!DOCTYPE html><html lang=»en»><body><script>
var foo = {
func1:function(bar){
bar();
console.log(this);//the this keyword here will be a reference to foo object
}
};
foo.func1(function(){console.log(this)});
</script></body></html>
|
Теперь вы никогда не забудете: this
значение всегда будет ссылкой на объект head, когда его основная функция инкапсулирована внутри другой функции или вызывается в контексте другой функции (опять же, это исправлено в ECMAScript 5).
Работа вокруг проблемы вложенных функций
Чтобы значение this
не потерялось, вы можете просто использовать цепочку областей действия, чтобы сохранить ссылку на this
в родительской функции. Приведенный ниже код демонстрирует, как, используя переменную с таким названием и используя ее область видимости, мы можем лучше отслеживать контекст функции.
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
|
<!DOCTYPE html><html lang=»en»><body><script>
var myObject = {
myProperty:’Icanseethelight’,
myMethod:function() {
var that=this;
var helperFunction function() { //childfunction
//logs ‘I can see the light’ via scope chain because that=this
console.log(that.myProperty);
console.log(this);
}();
}
}
myObject.myMethod();
</script></body></html>
|
Контролировать значение this
Значение this
обычно определяется из контекста, в котором вызывается функция (кроме случаев, когда используется new
ключевое слово — подробнее об этом через минуту), но вы можете перезаписать / контролировать значение this
с помощью apply()
или call()
чтобы определить, на какой объект this
указывает при вызове функции. Использование этих методов все равно, что сказать: « Эй, вызовите функцию X, но скажите функции использовать объект Z в качестве значения для this
. При этом способ по умолчанию, в котором JavaScript определяет значение this
, переопределяется.
Ниже мы создаем объект и функцию. Затем мы вызываем функцию через call()
так что значение this
внутри функции использует myObject
качестве контекста. Тогда операторы внутри функции myFunction
будут заполнять свойства myObject
вместо заполнения объекта head. Мы изменили объект, на который ссылается this
(внутри myFunction
).
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
|
<!DOCTYPE html><html lang=»en»><body><script>
var myObject = {};
var myFunction = function(param1, param2) {
//setviacall()’this’points to my Object when function is invoked
this.foo = param1;
this.bar = param2;
console.log(this);
};
myFunction.call(myObject, ‘foo’, ‘bar’);
console.log(myObject) // logs Object {foo = ‘foo’, bar = ‘bar’}
</script></body></html>
|
В приведенном выше примере мы используем call()
, но можно использовать apply()
. Разница между ними заключается в том, как передаются параметры для функции. Используя call()
, параметры просто разделяются запятыми. Используя apply()
, значения параметров передаются внутри array
. Ниже та же идея, но с использованием apply()
.
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
|
<!DOCTYPE html><html lang=»en»><body><script>
var myObject = {};
var myFunction = function(param1, param2) {
//set via apply(), this points to my Object when function is invoked
this.foo=param1;
this.bar=param2;
console.log(this);
};
myFunction.apply(myObject, [‘foo’, ‘bar’]);
console.log(myObject);
</script></body></html>
|
Здесь вам нужно убрать то, что вы можете переопределить способ по умолчанию, в котором JavaScript определяет значение
this
в области видимости функции.
Использование this
ключевого слова внутри определяемой пользователем функции конструктора
Когда функция вызывается с new
ключевым словом, значение this
— как указано в конструкторе — относится к самому экземпляру. Говорят по-другому: в функции конструктора мы можем использовать объект до того, как объект будет фактически создан. В этом случае значение по умолчанию для this
изменяется таким же образом, как при использовании call()
или apply()
.
Ниже мы настроили функцию конструктора Person
которая использует this
для ссылки на создаваемый объект. Когда создается экземпляр Person
, this.name
ссылается на вновь созданный объект и помещает свойство с именем name
в новый объект со значением из параметра ( name
), передаваемого в функцию конструктора.
01
02
03
04
05
06
07
08
09
10
11
|
<!DOCTYPE html><html lang=»en»><body><script>
var Person = function(name) {
this.name = name ||
}
var cody = new Person(‘Cody Lindley’);
console.log(cody.name);
</script></body></html>
|
Опять же, this
относится к «объекту, который должен быть», когда функция конструктора вызывается с использованием new
ключевого слова. Если бы мы не использовали ключевое слово new
, значением this
было бы контекст, в котором вызывается Person — в данном случае объект head. Давайте рассмотрим этот сценарий.
01
02
03
04
05
06
07
08
09
10
11
|
<!DOCTYPE html><html lang=»en»><body><script>
var Person = function(name) {
this.name=name||’johndoe’;
}
var cody = Person(‘Cody Lindley’);
console.log(cody.name);
console.log(window.name);
</script></body></html>
|
Ключевое слово this
Внутри метода-прототипа Относится к экземпляру конструктора.
При использовании в функциях, добавленных в свойство prototype
конструктора, this
относится к экземпляру, в котором вызывается метод. Скажем, у нас есть пользовательская функция конструктора Person()
. В качестве параметра требуется полное имя человека. В случае, если нам нужно получить доступ к полному имени человека, мы добавляем метод Person.prototype
в Person.prototype
, чтобы все экземпляры Person
наследовали метод. При использовании this
метод может ссылаться на экземпляр, вызывающий его (и, следовательно, на его свойства).
Здесь я демонстрирую создание двух объектов Person
( cody
и lisa
) и унаследованного метода whatIsMyFullName
который содержит ключевое слово this для доступа к экземпляру.
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
|
<!DOCTYPE html><html lang=»en»><body><script>
var Person = function(x){
if(x){this.fullName = x};
};
Person.prototype.whatIsMyFullName = function() {
return this.fullName;
}
var cody = new Person(‘cody lindley’);
var lisa = new Person(‘lisa lindley’);
// call the inherited whatIsMyFullName method, which uses this to refer to the instance
console.log(cody.whatIsMyFullName(), lisa.whatIsMyFullName());
/* The prototype chain is still in effect, so if the instance does not have a
fullName property, it will look for it in the prototype chain.
Below, we add a fullName property to both the Person prototype and the Object
prototype.
Object.prototype.fullName = ‘John Doe’;
var john = new Person();
console.log(john.whatIsMyFullName());
</script></body></html>
|
Суть в том, что ключевое слово this
используется для ссылки на экземпляры, когда они используются внутри метода, содержащегося в объекте- prototype
. Если экземпляр не содержит свойства, начинается поиск прототипа.
Примечания
— Если экземпляр или объект, на который указывает this
, не содержат ссылочного свойства, применяются те же правила, которые применяются к любому поиску свойства, и свойство будет «ищаться» в цепочке прототипов. Таким образом, в нашем примере, если свойство fullName
не содержалось в нашем экземпляре, тогда fullName
будет искать в Person.prototype.fullName
затем Object.prototype.fullName
.
Прочитайте книгу бесплатно!
Эта книга не о шаблонах проектирования JavaScript или реализации объектно-ориентированной парадигмы с помощью кода JavaScript. Он был написан не для того, чтобы отличать хорошие особенности языка JavaScript от плохих. Он не предназначен для полного справочного руководства. Он не предназначен для людей, плохо знакомых с программированием, или тех, кто совершенно не знаком с JavaScript. Это не поваренная книга рецептов JavaScript. Эти книги были написаны.