Стандарт ECMA-262 (JavaScript) в картинках, частина 2



У першій частині статті розглядалися структури execution context, lexical environment об'єкти Function. Друга частина присвячена використанню this.

Як працює this
execution context крім VariableEnvironment є поле ThisBinding. При пошуку звичайних змінних використовується VariableEnvironment, при зверненні через thisThisBinding. ThisBinding встановлюється в execution context в залежності від способу виклику функції. Якщо функція викликана через точку o.f(), ThisBinding буде вказувати на об'єкт o. У всіх інших випадках ThisBinding буде посилатися на global object. При цьому функція може бути одна і та ж.

Наприклад, створимо функцію print об'єкт o, а потім додамо посилання на функцію об'єкт. Виходить, що функцію можна викликати як
print()
та
o.print()
. ThisBinding при цьому буде відрізнятися. Якщо скопіювати посилання на o.print у нову змінну print2, то при виклику print2 ThisBinding буде вказувати на глобальний об'єкт, незважаючи на те, що посилання була взята з об'єкта.



Ще раз, якщо виклик "через точку", ThisBinding вказує на те, що до точки. В іншому випадку, ThisBinding посилається на global object.

Докладніше про те, як встановлюється this при виклику функції, можна прочитати в наступних розділах стандарту:

Що відбувається при виклику setTimeout
При використанні callback'ів передається посилання на функцію, але не на об'єкт. Тому при виклику callback'а this буде вказувати на global object.

У випадку з setTimout першим аргументом передається посилання на функцію func. Після зазначеної затримки setTimeout її викликає. Виклик здійснюється без крапки, просто як
func()
, ThisBinding вказує на global object.



Стандартний спосіб вирішити цю задачу – зберегти на об'єкт environment допоміжної функції. Маючи посилання на об'єкт, можна скористатися функцією Function.prototype.call. При виклику функції з допомогою call можна явно вказати посилання, яка буде записана в ThisBinding контексту.

Так при виклику
print.call(o)
буде створений контекст, в ThisBinding якого буде записана посилання на o.

Така функція як правило називається bind і може бути реалізована наступним чином.

...
// bind повертає функцію, яка викликає функцію f
function bind(thisArg, f) {
return function wrapper() {
f.call(thisArg);
}
}

// користуватися bind треба так
setTimeout(bind(o, o.print), 1);

При виклику функції bind створюється environment об'єкт Function з назвою wrapper. thisArg і f зберігаються посилання на об'єкт і функцію print. scope нового об'єкта Function кладеться посилання на environment. При виході з bind контекст знищується, але environment залишається, так як на нього посилається wrapper.

enter image description here

При виклику setTimeout за delay мілісекунд викликається функція wrapper. При створенні environment у поле outer записується значення scope викликається функції. В даному випадку outer буде посилатися на environment bind. При виклику
f.call(thisArg)
wrapper обидві змінні будуть знайдені в цьому environment. Потім при виклику call буде створений контекст для print, ThisBinding буде вказувати на o.

enter image description here

В JavaScript є стандартний метод Function.prototype.bind, який дозволяє запам'ятати не тільки this, але і аргументи функції.

// замість
setTimeout(bind(o, o.print), 1);

// правильніше використовувати стандартний bind
setTimeout(o.print.bind(o), 1);

У наступній частині поговоримо про прототипном спадкування.

Джерело: Хабрахабр
  • avatar
  • 0

0 коментарів

Тільки зареєстровані та авторизовані користувачі можуть залишати коментарі.