Ответ 1
Javascript - это язык позднего связывания. На самом деле, это очень поздно связывать. Мало того, что this
не связано во время компиляции, он даже не обязан во время исполнения (как и большинство других языков позднего связывания). В JavaScript this
связано во время разговора.
Правила связывания сильно отличаются от большинства других ОО-языков, поэтому кажется, что это сбивает с толку многих людей, не знакомых с javascript.
В принципе, как и где вы используете this
в коде не влияет на this
поведение (это не имеет значения, если он автономную функцию, литерал объекта и т.д.), что определяет значение this
в том, как вы вызываете функцию.
Правила таковы:
1 - Когда функция вызывается как конструктор, новый объект создается и this
связано с этим объектом. Например:
function Foo () {
this.bar = 1; // when called with the new keyword
// this refers to the object just created
}
new Foo().bar;
2 - При вызове в качестве метода объекта this
относится к объекту, которому принадлежит метод. В основном имя перед последней точкой. Например:
foo.bar = 1;
foo.baz = function () {
alert(this.bar); // this refers to foo when called as foo.baz()
}
foo.baz();
3 - Если используется вне какой-либо функции или если функция не вызывается как метод, this
относится к глобальному объекту. Спецификация javascript не дает имени глобальному объекту, кроме того, что он существует, но для браузеров он традиционно называется window
. Например:
bar = 1;
alert(this.bar); // this refers to the global object
foo = {
bar: this.bar // also global object
}
function foofoo () {
alert(this.bar); // also refers to the global object
}
foofoo();
4 - В обработчике события (например, onclick и т.д.) this
относится к элементу DOM, который вызвал событие. Или для событий, не связанных с DOM, таких как setTimeout
или XMLHTTPRequest
, this
относится к глобальному объекту. Например:
foo.bar = 1;
foo.baz = function () {
alert(this.bar); // this would normally be foo but if this
// function is assigned to an event it would
// point to the element that triggered the event
}
somediv.bar = 2;
somediv.onclick = foo.baz; // clicking on somedive alerts 2 instead of 1
5. Наконец, когда функция вызывается с использованием методов call()
или apply()
this
можно переназначить на что угодно (google "mdn function.prototype.call"). Таким образом, любой объект в javascript может позаимствовать/украсть методы других объектов. Например:
cat = {
type: "cat",
explain: function () {
return "I am a " + this.type;
}
}
dog = {
type: "dog"
}
cat.explain.call(dog); // returns "I am a dog"
С Function.bind()
в современных реализациях javascript у нас теперь есть другое правило:
6 - Функции также могут явно связать this
с объектом, используя метод bind()
. Метод bind
возвращает новый экземпляр функции, где this
связано с аргументом, переданным для bind
. Например:
function explain () {
return "I am a " + this.type;
}
dog = {
type: "dog"
}
var dog_explain = explain.bind(dog);
dog_explain(); // returns "I am a dog"
В ECMAscript 5 введен строгий режим, который меняет значение этого в функциях, которые не вызываются как методы, не вызываются с помощью call или apply, поэтому мы должны добавить новое правило:
7 - Когда в строгом режиме, this
не может ссылаться на глобальный объект (окно в браузере). Поэтому, когда функция не вызывается как метод или this
не связано ни к чему вручную с помощью call
или apply
или bind
то this
становится undefined
:
"use strict";
function foo () {
return this;
}
foo(); // returns undefined instead of the global object
В ECMAscript 6 введены функции стрелок. Функции стрелок меняют поведение при раннем связывании.
8 - В функциях стрелок this
связано в то время, когда функция объявлена. Так this
в следующем коде:
var x = () => {return this};
ведет себя так, как будто функция объявлена как следующий код:
var x = function () {return this}.bind(this);
Обратите внимание, что поскольку функции this
in arrow связаны во время объявления функции, вы не можете использовать функции arrow, если хотите использовать наследование. Это потому, что this
в функции всегда будет указывать на родительский объект и никогда не будет указывать на дочерний объект. Это означает, что единственный способ заставить наследование работать с функцией стрелки - это переопределить все функции стрелки из родительского объекта.