в 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, включая Arrays и Functions). Возьмите следующий код для примера.
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
Так что да, поскольку functions также 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для решения проблем вложенных областей.