Javascript's this
Простой вызов функции
Рассмотрим следующую функцию:
function foo() {
console.log("bar");
console.log(this);
}
foo(); // calling the function
Обратите внимание, что мы запускаем это в обычном режиме, то есть строгий режим не используется.
При работе в браузере значение this
регистрируется как window
. Это связано с тем, что window
является глобальной переменной в области видимости веб-браузера.
Если вы запустите этот же кусок кода в среде, подобной node.js, this
будет ссылаться на глобальную переменную в вашем приложении.
Теперь, если мы запустим это в строгом режиме, добавив оператор "use strict";
в начало объявления функции, this
больше не будет ссылаться на глобальную переменную ни в одной из сред. Это сделано, чтобы избежать путаницы в строгом режиме. this
будет в этом случае просто записывать undefined
, потому что это то, что есть, оно не определено.
В следующих случаях мы увидим, как манипулировать значением this
.
Вызов функции для объекта
Есть разные способы сделать это. Если вы вызывали нативные методы в Javascript, такие как forEach
и slice
, вы уже должны знать, что переменная this
в этом случае относится к Object
, для которого вы вызвали эту функцию (обратите внимание, что в javascript, все равно Object
, включая Array
с и Function
с). Возьмите следующий код для примера.
var myObj = {key: "Obj"};
myObj.logThis = function () {
// I am a method
console.log(this);
}
myObj.logThis(); // myObj is logged
Если Object
содержит свойство, которое содержит Function
, это свойство называется методом. При вызове этого метода его переменная this
всегда будет иметь значение Object
, с которым она связана. Это верно как для строгих, так и для нестрогих режимов.
Обратите внимание, что если метод хранится (или, скорее, копируется) в другой переменной, ссылка на this
больше не сохраняется в новой переменной. Например:
// continuing with the previous code snippet
var myVar = myObj.thisMethod;
myVar();
// logs either of window/global/undefined based on mode of operation
Учитывая более практичный сценарий:
var el = document.getElementById('idOfEl');
el.addEventListener('click', function() { console.log(this) });
// the function called by addEventListener contains this as the reference to the element
// so clicking on our element would log that element itself
Ключевое слово new
Рассмотрим функцию конструктора в Javascript:
function Person (name) {
this.name = name;
this.sayHello = function () {
console.log ("Hello", this);
}
}
var awal = new Person("Awal");
awal.sayHello();
// In `awal.sayHello`, `this` contains the reference to the variable `awal`
Как это работает? Что ж, давайте посмотрим, что произойдет, когда мы используем ключевое слово new
.
- Вызов функции с ключевым словом
new
немедленно инициализирует Object
типа Person
.
- Конструктор этого
Object
имеет свой конструктор, установленный на Person
. Также обратите внимание, что typeof awal
вернет только Object
.
- Этому новому
Object
будет присвоен прототип Person.prototype
. Это означает, что любой метод или свойство в прототипе Person
будет доступно для всех экземпляров Person
, включая awal
.
- Теперь вызывается сама функция
Person
; this
является ссылкой на недавно построенный объект awal
.
Довольно просто, а?
Обратите внимание, что в официальной спецификации ECMAScript нигде не говорится, что такие типы функций являются действительными constructor
функциями. Это просто обычные функции, и new
может использоваться для любой функции. Просто мы используем их как таковые, и поэтому мы называем их только такими.
Вызов функций в функциях: call
и apply
Так что да, поскольку function
s также Objects
(и фактически переменные первого класса в Javascript), даже функции имеют методы, которые ... ну, сами функции.
Все функции наследуются от глобального Function
, и два из его многочисленных методов - call
и apply
, и оба могут использоваться для манипулирования значением this
в функции, для которой они вызваны.
function foo () { console.log (this, arguments); }
var thisArg = {myObj: "is cool"};
foo.call(thisArg, 1, 2, 3);
Это типичный пример использования call
. В основном он принимает первый параметр и устанавливает this
в функции foo
как ссылку на thisArg
. Все остальные параметры, переданные в call
, передаются в функцию foo
в качестве аргументов.
Таким образом, приведенный выше код будет записывать {myObj: "is cool"}, [1, 2, 3]
в консоли. Довольно хороший способ изменить значение this
в любой функции.
apply
- это почти то же самое, что и call
. Примите, что он принимает только два параметра: thisArg
и массив, содержащий аргументы, которые должны быть переданы функции. Таким образом, вышеуказанный call
вызов может быть переведен на apply
следующим образом:
foo.apply(thisArg, [1,2,3])
Обратите внимание, что call
и apply
могут переопределять значение this
, установленное вызовом метода точки, который мы обсуждали во втором пуле.
Достаточно просто:)
Представляя .... bind
!
bind
является братом call
и apply
. Это также метод, унаследованный всеми функциями от глобального конструктора Function
в Javascript. Разница между bind
и call
/ apply
заключается в том, что и call
, и apply
фактически вызовут функцию. bind
, с другой стороны, возвращает новую функцию с предустановкой thisArg
и arguments
. Давайте рассмотрим пример, чтобы лучше понять это:
function foo (a, b) {
console.log (this, arguments);
}
var thisArg = {myObj: "even more cool now"};
var bound = foo.bind(thisArg, 1, 2);
console.log (typeof bound); // logs `function`
console.log (bound);
/* logs `function () { native code }` */
bound(); // calling the function returned by `.bind`
// logs `{myObj: "even more cool now"}, [1, 2]`
Видите разницу между тремя? Это тонко, но они используются по-разному. Подобно call
и apply
, bind
также переопределяет значение this
, установленное при вызове точечного метода.
Также обратите внимание, что ни одна из этих трех функций не изменяет исходную функцию. call
и apply
вернут значение из только что созданных функций, а bind
вернет саму недавно созданную функцию, готовую к вызову.
Дополнительные материалы, скопируйте это
Иногда вам не нравится тот факт, что this
изменяется с областью действия, особенно с вложенной областью действия. Посмотрите на следующий пример.
var myObj = {
hello: function () {
return "world"
},
myMethod: function () {
// copy this, variable names are case-sensitive
var that = this;
// callbacks ftw \o/
foo.bar("args", function () {
// I want to call `hello` here
this.hello(); // error
// but `this` references to `foo` damn!
// oh wait we have a backup \o/
that.hello(); // "world"
});
}
};
В приведенном выше коде мы видим, что значение this
изменилось с вложенной областью действия, но мы хотели получить значение this
от исходной области действия. Итак, мы «скопировали» this
в that
и использовали копию вместо this
. Умно, а?
Индекс:
- Что содержится в
this
по умолчанию?
- Что если мы вызовем функцию как метод с нотацией Object-dot?
- Что если мы используем ключевое слово
new
?
- Как нам манипулировать
this
с call
и apply
?
- Использование
bind
.
- Копирование
this
для решения проблем с вложенными областями.