Ця стаття охоплює складну тему, щоб краще зрозуміти певні крайні випадки.
Це не є важливим. Багато досвідчених розробників живуть добре, не знаючи цього. Прочитайте, якщо ви хочете знати, як певні речі працюють під капотом.
Динамічно оцінений виклик методу може втратити this.
Наприклад:
let user = {
name: "Іван",
hi() { alert(this.name); },
bye() { alert("До побачення"); }
};
user.hi(); // працює
// тепер викличмо user.hi або user.bye залежно від назви
(user.name == "John" ? user.hi : user.bye)(); // Помилка!
На останньому рядку є умовний оператор, який вибирає або user.hi або user.bye. У цьому випадку результат – user.hi.
Потім метод негайно викликається за допомогою дужок (). Але це працює неправильно!
Як ви бачите, виклик призводить до помилки, тому що значення "this" всередині виклику стає undefined.
Це працює (метод об’єкта через крапку):
user.hi();
Це ні (динамічно оцінений метод):
(user.name == "John" ? user.hi : user.bye)(); // Помилка!
Чому? Якщо ми хочемо зрозуміти, чому це трапляється, загляньмо під капот, як працює виклик obj.method().
Пояснення Посилального Типу
Дивлячись уважно, ми можемо помітити дві операції в інструкції obj.method():
- По-перше, крапка
'.'витягує властивістьobj.method. - Потім дужки
()виконуйте її.
Отже, як інформація про this передається з першої частини до другої?
Якщо ми поставимо ці операції на окремі рядки, то this напевно буде втрачено:
let user = {
name: "John",
hi() { alert(this.name); }
};
// розділимо отримання та виклик методу на два рядки
let hi = user.hi;
hi(); // Помилка, тому що this -- це undefined
Тут hi = user.hi поміщає функцію в змінну, а потім на останньому рядку, ця змінна повністю автономна, і тому не має this.
Щоб зробити виклик user.hi() робочим, JavaScript використовує трюк – крапка '.' повертає не функцію, а значення спеціального посилальний типу.
Посилальний тип – це “тип специфікації”. Ми не можемо явно використовувати його, але він використовується всередині мови.
Значення посилального типу – це комбінація трьох значення (base, name, strict), де:
base– це об’єкт.name– це назва властивості.strict– це true якщо дієuse strict.
Результат доступу до властивості user.hi є не функцією, а значенням посилального типу. Для user.hi у суворому режимі це:
// Значення посилального типу
(user, "hi", true)
Коли дужки () викликаються з посилальним типом, вони отримують повну інформацію про об’єкт та його метод, і можуть встановити правильний this (=user у даному випадку).
Посилальний тип – це особливий “посередницький” внутрішній тип, який використовується з метою передачі інформації від крапки . до дужок виклику ().
Будь-яка інша операція, наприклад присвоєння hi = user.hi в цілому відкидає посилальний тип та приймає значення user.hi (функції) і передає його. Отже, будь-яка подальша операція “втрачає” this.
Отже, як результат, значення this передається правильно тільки тоді, якщо функція викликається безпосередньо за допомогою крапки obj.method() або синтаксису квадратних дужок obj['method']() (вони роблять одне й те ж саме). Існують різні способи розв’язання цієї проблеми, як func.bind().
Підсумки
Посилальний тип – це внутрішній тип мови.
Читання властивості, наприклад, крапкою . в obj.method() повертає не саме значення властивості, але спеціальне значення “посилального типу”, яке зберігає як значення властивості, так і об’єкт, з якою він був взятий.
Це використовується для подальшого виклику методу за допомогою (), щоб отримати об’єкт і встановити this до цього.
Для всіх інших операцій, посилальний тип автоматично стає значенням властивості (функцією у нашому випадку).
Вся ця механіка прихована від наших очей. Це лише важливо в тонких випадках, наприклад, коли метод отримується динамічно з об’єкта, використовуючи вираз.


Коментарі
<code>, для кількох рядків – обгорніть їх тегом<pre>, для понад 10 рядків – використовуйте пісочницю (plnkr, jsbin, codepen…)