в JavaScript 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
s и Function
s). Возьмите следующий код для примера.
var myObj = {key: "Obj"};
myObj.logThis = function () {
// I am a method
console.log(this);
}
myObj.logThis(); // myObj is logged
Если an Object
содержит свойство, которое содержит Function
, свойство называется методом. Этот метод, при вызове, всегда будет иметь свою this
переменную равной тому, с которой Object
он связан. Это верно как для строгих, так и для нестрогих режимов.
Обратите внимание, что если метод хранится (или, скорее, копируется) в другой переменной, ссылка на this
более не сохраняется в новой переменной. Например:
// continuing with the previous code snippet
var myVar = myObj.logThis;
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
для решения проблем вложенных областей.