Что вы должны знать о this
this
(он же «контекст») - это специальное ключевое слово внутри каждой функции, и его значение зависит только от того, как была вызвана функция, а не от того, как / когда / где она была определена. На него не влияют лексические области, как и другие переменные (кроме функций со стрелками, см. Ниже). Вот некоторые примеры:
function foo() {
console.log(this);
}
// normal function call
foo(); // `this` will refer to `window`
// as object method
var obj = {bar: foo};
obj.bar(); // `this` will refer to `obj`
// as constructor function
new foo(); // `this` will refer to an object that inherits from `foo.prototype`
Чтобы узнать больше this
, взгляните на документацию MDN .
Как правильно сослаться на this
Не использовать this
Вы на самом деле не хотите получать доступ, this
в частности, к объекту, на который он ссылается . Вот почему простое решение - просто создать новую переменную, которая также ссылается на этот объект. Переменная может иметь любое имя, но наиболее распространенными являются self
и that
.
function MyConstructor(data, transport) {
this.data = data;
var self = this;
transport.on('data', function() {
alert(self.data);
});
}
Поскольку self
это обычная переменная, она подчиняется лексическим правилам области видимости и доступна внутри обратного вызова. Это также имеет то преимущество, что вы можете получить доступ к this
значению самого обратного вызова.
Явный набор this
обратного вызова - часть 1
Может показаться, что вы не контролируете значение, this
потому что его значение устанавливается автоматически, но на самом деле это не так.
Каждая функция имеет метод .bind
[docs] , который возвращает новую функцию с this
привязкой к значению. Функция действует точно так же, как и та, которую вы вызывали .bind
, только то, что this
было установлено вами. Независимо от того, как или когда эта функция вызывается, this
всегда будет ссылаться на переданное значение.
function MyConstructor(data, transport) {
this.data = data;
var boundFunction = (function() { // parenthesis are not necessary
alert(this.data); // but might improve readability
}).bind(this); // <- here we are calling `.bind()`
transport.on('data', boundFunction);
}
В этом случае, мы являемся обязательными для выполнения обратного вызова - х this
к значению MyConstructor
х гг this
.
Примечание. При связывании контекста для jQuery используйте вместо него jQuery.proxy
[docs] . Причина этого заключается в том, что вам не нужно сохранять ссылку на функцию при отмене привязки обратного вызова события. JQuery обрабатывает это внутренне.
ECMAScript 6 представляет функции стрелок , которые можно рассматривать как лямбда-функции. У них нет собственной this
привязки. Вместо этого this
ищется в области видимости, как обычная переменная. Это означает, что вам не нужно звонить .bind
. Это не единственное специальное поведение, которое они имеют, пожалуйста, обратитесь к документации MDN для получения дополнительной информации.
function MyConstructor(data, transport) {
this.data = data;
transport.on('data', () => alert(this.data));
}
Набор this
обзвона - часть 2
Некоторые функции / методы, которые принимают обратные вызовы, также принимают значение, на которое this
должен ссылаться обратный вызов . По сути, это то же самое, что связывать его самостоятельно, но функция / метод делает это за вас. Array#map
[Документы] такой метод. Его подпись:
array.map(callback[, thisArg])
Первый аргумент - это обратный вызов, а второй аргумент - это значение, на которое this
должно ссылаться. Вот надуманный пример:
var arr = [1, 2, 3];
var obj = {multiplier: 42};
var new_arr = arr.map(function(v) {
return v * this.multiplier;
}, obj); // <- here we are passing `obj` as second argument
Примечание. Вопрос о том, можете ли вы передать значение, this
обычно упоминается в документации к этой функции / методу. Например, метод jQuery [docs]$.ajax
описывает параметр под названием context
:
Этот объект станет контекстом всех обратных вызовов Ajax.
Распространенная проблема: использование объектных методов в качестве обратных вызовов / обработчиков событий
Другое распространенное проявление этой проблемы - когда объектный метод используется в качестве обработчика обратного вызова / события. Функции являются первоклассными гражданами в JavaScript, а термин «метод» - это просто разговорный термин для функции, которая является значением свойства объекта. Но эта функция не имеет конкретной ссылки на свой «содержащий» объект.
Рассмотрим следующий пример:
function Foo() {
this.data = 42,
document.body.onclick = this.method;
}
Foo.prototype.method = function() {
console.log(this.data);
};
Функция this.method
назначается в качестве обработчика события щелчка, но если document.body
щелкнуть, зарегистрированное значение будет undefined
, поскольку внутри обработчика события this
ссылается на document.body
, а не на экземпляр Foo
.
Как уже упоминалось в начале, то , что this
относится к зависит от того, как функция называется , не так, как она определена .
Если бы код был похож на следующий, может быть более очевидно, что функция не имеет неявной ссылки на объект:
function method() {
console.log(this.data);
}
function Foo() {
this.data = 42,
document.body.onclick = this.method;
}
Foo.prototype.method = method;
Решение такое же, как упомянуто выше: если доступно, используйте .bind
для явного связывания this
с конкретным значением
document.body.onclick = this.method.bind(this);
или явно вызвать функцию как «метод» объекта, используя анонимную функцию в качестве обработчика обратного вызова / события, и назначить объект ( this
) другой переменной:
var self = this;
document.body.onclick = function() {
self.method();
};
или используйте функцию стрелки:
document.body.onclick = () => this.method();