Про що слід знати 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до значення MyConstructors 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 [docs] - такий метод. Його підпис:
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();