Когда создается функция, создается ключевое слово с именем this
(за кулисами), которое ссылается на объект, в котором работает функция. Иными словами, this
доступно для области его функции, но является ссылкой на объект, функцией которого является свойство или метод.
Давайте посмотрим на объект cody
из предыдущей статьи:
Образец: sample98.html
01
02
03
04
05
06
07
08
09
10
11
12
|
<!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
мы получаем доступ к свойству gender
с помощью точечной нотации ( cody.gender
) на cody
объекте cody
. Это можно переписать, используя this
для доступа к объекту cody
потому что this
указывает на объект cody
.
Образец: sample99.html
01
02
03
04
05
06
07
08
09
10
11
12
|
<!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
вызывается из глобальной области видимости, this
относится к объекту window
. Когда он вызывается как метод myObject, this
относится к myObject
.
Поскольку myObject
имеет свойство с именем foo
, это свойство используется.
Образец: sample100.html
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 the sayFoo function.
myObject.sayFoo = sayFoo;
myObject.sayFoo();
sayFoo();
</script></body></html>
|
Ясно, что значение this
зависит от контекста, в котором вызывается функция. Учтите, что и myObject.sayFoo
и sayFoo
указывают на одну и ту же функцию. Однако, в зависимости от того, откуда (контекст) sayFoo()
, значение this
может быть другим.
Если это поможет, вот тот же код с явно используемым объектом head ( window
).
Образец: sample101.html
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
|
<!DOCTYPE html><html lang=»en»><body><script>
window.foo = ‘foo’;
window.myObject = { foo: ‘I am myObject.foo’ };
window.sayFoo = function () {
console.log(this.foo);
};
window.myObject.sayFoo = window.sayFoo;
window.myObject.sayFoo();
window.sayFoo();
</script></body></html>
|
Убедитесь, что, передавая функции или имея несколько ссылок на функцию, вы понимаете, что значение этого параметра будет меняться в зависимости от контекста, в котором вы вызываете функцию.
Все переменные, кроме this
и arguments
следуют лексической области видимости
Ключевое слово this
относится к объекту Head во вложенных функциях.
Вам может быть интересно, что происходит с this
когда он используется внутри функции, которая содержится внутри другой функции. Плохая новость в ECMA 3, this
теряет свою актуальность и относится к головному объекту (объект window
в браузерах), а не к объекту, внутри которого определена функция.
В следующем коде this
внутри func2
и func3
теряет свой путь и ссылается не на myObject
а вместо этого на объект head.
Образец: sample102.html
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);
var func2 = function () {
console.log(this) // Logs window, and will do so from this point on.
var func3 = function () {
console.log(this);
} ();
} ();
}
}
myObject.func1();
</script></body></html>
|
Хорошая новость заключается в том, что это будет исправлено в ECMAScript 5. На данный момент вы должны знать об этом затруднительном положении, особенно когда вы начинаете передавать функции в качестве значений другим функциям.
Рассмотрим следующий пример и то, что происходит при передаче анонимной функции в foo.func1
. Когда анонимная функция вызывается внутри foo.func1
(функция внутри функции), значение this
внутри анонимной функции будет ссылкой на объект head.
Образец: sample103.html
01
02
03
04
05
06
07
08
09
10
11
12
|
<!DOCTYPE html><html lang=»en»><body><script>
var foo = {
func1: function (bar) {
bar();
console.log(this);
}
}
foo.func1(function () { console.log(this) });
</script></body></html>
|
Теперь вы никогда не забудете: this
значение всегда будет ссылкой на объект head, когда его основная функция инкапсулирована внутри другой функции или вызывается в контексте другой функции (опять же, это исправлено в ECMAScript 5).
Обойти проблему вложенных функций, используя цепочку областей видимости
Чтобы значение this
не потерялось, вы можете просто использовать цепочку областей действия, чтобы сохранить ссылку на this
в родительской функции. В следующем примере показано, как, используя переменную с таким названием и используя ее область видимости, мы можем лучше отслеживать контекст функции.
Образец: sample104.html
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: ‘I can see the light’,
myMethod : function(){
var that = this;
var helperFunction = function() { // Child function.
// 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
помощью call()
или apply()
Значение this
обычно определяется из контекста, в котором вызывается функция (кроме случаев, когда новое ключевое слово используется более подробно об этом через минуту), но вы можете перезаписать и контролировать значение this
с помощью apply()
или call()
для определить, на какой объект this
указывает при вызове функции. Использование этих методов все равно что сказать: «Эй, вызовите функцию X, но скажите функции использовать объект Z в качестве значения для this
». При этом способ по умолчанию, при котором JavaScript определяет значение this
, переопределяется.
В следующем примере мы создаем объект и функцию. Затем мы вызываем функцию через call()
так что значение this
внутри функции использует myObject
качестве контекста. Тогда операторы внутри функции myFunction
будут заполнять свойства myObject
вместо заполнения объекта head. Мы изменили объект, на который ссылается this
(внутри myFunction
).
Образец: sample105.html
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) {
// Set via call(), ‘this’ points to myObject when function is invoked.
this.foo = param1;
this.bar = param2;
console.log(this) // Logs Object {foo = ‘foo’, bar = ‘bar’}
};
myFunction.call(myObject, ‘foo’, ‘bar’);
console.log(myObject) // Logs Object {foo = ‘foo’, bar = ‘bar’}
</script></body></html>
|
В предыдущем примере мы использовали call()
, но можно использовать apply()
. Разница между ними заключается в том, как передаются параметры для функции. Используя call()
, параметры являются просто значениями, разделенными запятыми. Используя apply()
, значения параметров передаются внутри массива, как показано в следующем примере.
Образец: sample106.html
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) {
// Set via apply(), this points to myObject when function is invoked.
this.foo = param1;
this.bar = param2;
console.log(this) // Logs Object {foo = ‘foo’, bar = ‘bar’}
};
myFunction.apply(myObject, [‘foo’, ‘bar’]);
console.log(myObject) // Logs Object {foo = ‘foo’, bar = ‘bar’}
</script></body></html>
|
Здесь вам нужно узнать, что вы можете переопределить способ по умолчанию, при котором JavaScript определяет значение this
в области действия функции.
Использование this
ключевого слова внутри определяемой пользователем функции конструктора
Когда функция вызывается с new
ключевым словом, значение this
указанное в конструкторе, относится к самому экземпляру. Говорят по-другому: в функции конструктора мы можем использовать объект до того, как объект будет создан. В этом случае значение по умолчанию изменяется таким же образом, как при использовании call()
или apply()
.
В следующем примере мы настроили функцию конструктора Person
которая использует this
для ссылки на создаваемый объект. Когда создается экземпляр Person
, this.name
ссылается на вновь созданный объект и помещает свойство с именем name в новый объект со значением из параметра ( name
), передаваемого в функцию конструктора.
Образец: sample107.html
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. Давайте рассмотрим следующий сценарий:
Образец: sample108.html
01
02
03
04
05
06
07
08
09
10
11
12
13
|
<!DOCTYPE html><html lang=»en»><body><script>
var Person = function (name) {
this.name = name ||
}
var cody = Person(‘Cody Lindley’);
console.log(cody.name);
console.log(window.name);
</script></body></html>
|
Ключевое слово this
внутри метода-прототипа относится к экземпляру конструктора
При использовании в функциях, добавленных в свойство prototype
конструкторов, this
относится к экземпляру, в котором вызывается метод. Скажем, у нас есть пользовательская функция конструктора Person()
. В качестве параметра требуется полное имя лица. В случае, если нам нужно получить доступ к полному имени человека, мы добавляем метод whatIsMyFullName
к Person.prototype
чтобы все экземпляры Person
наследовали метод. При использовании this
метод может ссылаться на экземпляр, вызывающий его (и, следовательно, на его свойства).
Здесь я демонстрирую создание двух объектов Person
( cody
и lisa
) и унаследованного метода whatIsMyFullName
который содержит ключевое слово this для доступа к экземпляру.
Образец: sample109.html
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
<!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.
Object.prototype.fullName = ‘John Doe’;
var john = new Person();
console.log(john.whatIsMyFullName());
</script></body></html>
|
Вывод
Концепция, которую следует здесь отбросить, заключается в that
что ключевое слово this используется для ссылки на экземпляры, когда они используются внутри метода, содержащегося в объекте- prototype
. Если экземпляр не содержит свойства, начинается поиск прототипа.
Если экземпляр или объект, на который указывает this
, не содержат свойства, на которое ссылаются, применяются те же правила, которые применяются к любому поиску свойства, и свойство будет «найдено» в цепочке прототипов. Таким образом, в нашем примере, если свойство fullName
не содержалось в нашем экземпляре, для fullName
Person.prototype.fullName
, а затем Object.prototype.fullName
.