a)
const itemHandler = {
addItem: function (arg) {
let a = this.helper(arg);
},
helper: (prop) => {
return prop;
},
};
b)
const itemHandler = {
addItem: (arg) => {
let a = this.helper(arg);
},
helper: (prop) => {
return prop;
},
};
c) Обидва правильні
Розгорнути правильну відповідь з поясненням
Чергове питання з рубрики "захоплюючий" javascript. У цьому питанні перевіряється, чи знаєш ти, що таке контекст функції і як працюють arrow functions. Це питання часто виникає при роботі з React, коли ти намагаєшся передати метод через властивості компонента, і не можеш зрозуміти, чому в методі компонента стан іншого компонента 🤷🏿 Але не хвилюйтеся, зараз ми з цим розберемося 😉
Коли ми оголошуємо метод/функцію через function, то при її виклику буде використовуватися контекст (this), в якому вона викликається. Але варто нам записати функцію в інший об'єкт, this відразу зміниться. Щоб це зрозуміти, давайте проведемо експеримент:
// оголошуємо функцію
function f() {
return this;
}
// записуємо ії в об’єкт
const a = { f: f };
// перезаписуємо ії з об’єкта
const b = {};
b.f = a.f;
// викликаємо їі з методу об’єкта
const c = {
f: function () {
return f();
},
};
// перевіряємо
console.log(f() === globalThis); // true
console.log(a.f() === a); // true
console.log(b.f() === b); // true
console.log(c.f() === globalThis); // true
У першому варіанті повернеться globalThis - це глобальний контекст (у браузері це window). Іншими словами, якщо функція викликається не як метод об'єкта, то this буде глобальний. Але варто нам записати її в якийсь об'єкт, то this зміниться. Крім того в JS у функції є 3 методи, які дозволяють змінити контекст насильно:
f.apply(context, [...args])
- викличе функцію з контекстом context, значення аргументів функції будуть відповідати елементам масивуargs
f.call(context, ...args)
- викличе функцію з контекстом context, аргументи (args) передаються послідовноf.bind(context, ...args)
- змінить контекст і поверне нову функцію, без виклику
Метод bind також дозволяє нам зафіксувати контекст, тобто якщо ми запишемо функцію в інший об'єкт, контекст не зміниться:
const a = {};
const d = {};
d.f = f.bind(a);
console.log(d.f() === a); // true
Arrow function
у свою чергу успадковує контекст з місця свого визначення. Причому, якщо ми визначимо arrow function
у інший arrow function
, то js буде підніматися вгору, поки не зустріне перший-ліпший контекст 🤯 Давайте подивимося, що буде якщо f визначити через arrow function
:
const f = () => this;
const a = { f: f };
const b = {};
b.f = a.f;
const c = {
f: function () {
return f();
},
};
console.log(f() === globalThis); // true
console.log(a.f() === globalThis); // true
console.log(b.f() === globalThis); // true
console.log(c.f() === globalThis); // true
Тобто на відміну від function
, контекст у arrow function
не змінився, незалежно від того, де ми її викликали. Давайте розглянемо ще один приклад:
const contextA = {
f1: function () {
const foo = () => this;
return foo(); // викликаємо
},
f2: function () {
const bar = () => this;
return bar; // не викликаємо
},
};
const contextB = {};
contextB.f1 = contextA.f1; // (1)
contextB.f2 = contextA.f2(); // (2)
console.log(contextA.f1() === contextA); // true
console.log(contextA.f2()() === contextA); // true
console.log(contextB.f1() === contextB); // true
console.log(contextB.f2() === contextA); // true
Зверніть увагу, що коли ми поміняли контекст методу f1 з contextA на contextB (1), то він автоматично змінився для внутрішньої foo, тому що контекст визначення foo змінився.
А в методі f2 ми визначили функцію bar в контексті contextA та привласнили її contextB (2), при цьому контекст не змінився, тому що місце визначення залишилося в contextA.
Отже, щоб бути впевненим у тому, що контекст функції буде той, який ми очікуємо, незалежно від того, де ми будемо її викликати, ми можемо використовувати або bind(), або arrow function.
Фуух, це було складно, але я думаю, тепер ви готові відповісти на питання 🙂 У варіанті b) ми визначаємо функцію в глобальному контексті, а не в контексті об'єкта, а оскільки в globalThis ми не визначили метод helper(), то буде помилка this.helper is not a function. Значить, варіант b) неправильний, відповідно і відповідь c) - теж. Залишається тільки a), ну а ми й без цього вже знаємо, що якщо метод визначений через function, то контекст буде той, де ми цей метод викличемо, в даному випадку на об'єкті itemHandler.
Щоб краще підготуватися до вступного тесту, рекомендую ознайомитися з функціями за наступними посиланнями:
https://javascript.info/function-expressions
https://javascript.info/arrow-functions-basics
https://javascript.info/javascript-specials
https://javascript.info/advanced-functions