Контекст функції
Контекст функції це об'єкт, на який посилається ключове слово this усередині функції під час її виконання. Головне правило: контекст визначається в момент виклику функції, а не в момент її оголошення.
Теорія
TL;DR
thisце посилання на об'єкт, у контексті якого зараз виконується функція.- Значення
thisзалежить від способу виклику, а не від місця оголошення. - Звичайний виклик: глобальний об'єкт (
windowу браузері) абоundefinedу strict mode. - Виклик як метод
obj.method(): контекстом стаєobj. - Виклик через
new: контекстом стає новий створений об'єкт. call,apply,bind: контекст задається вручну.- Стрілкові функції власного
thisне мають і беруть його з зовнішнього оточення.
Швидкий приклад
function showThis() {
console.log(this);
}
showThis(); // у звичайному виклику контекстом є глобальний об'єктУ браузері в нестрогому режимі:
this === window;У Node.js усередині звичайної функції в строгому режимі:
this === undefined;Контекст залежить від способу виклику
| Як викликаємо функцію | Яким буде this |
|---|---|
Звичайний виклик func() | Глобальний об'єкт (window) або undefined у strict mode |
Виклик як метод obj.method() | Об'єкт obj |
Через new | Новий створений об'єкт |
Через call / apply / bind | Вказаний вручну об'єкт |
| У стрілковій функції | this береться з зовнішнього контексту (лексично) |
Звичайний виклик, тобто глобальний контекст:
function test() {
console.log(this);
}
test(); // window (або undefined у strict mode)Виклик як метод об'єкта:
const user = {
name: "Alice",
sayHi() {
console.log(this.name);
},
};
user.sayHi(); // "Alice"Тут this посилається на об'єкт user, бо функцію викликано саме як user.sayHi().
Втрата контексту
const user = {
name: "Alice",
sayHi() {
console.log(this.name);
},
};
const hi = user.sayHi;
hi(); // undefined, this втраченоЧому так:
- Ми передали посилання на функцію, але не сам об'єкт.
- Виклик
hi()це звичайний виклик, без контекстуuser.
Виправлення через bind:
const hi = user.sayHi.bind(user);
hi(); // "Alice"Та сама пастка трапляється, коли метод передають як колбек: setTimeout(user.sayHi, 100) або button.addEventListener("click", user.sayHi).
Контекст при виклику через new
function User(name) {
this.name = name;
}
const u = new User("Alice");
console.log(u.name); // "Alice"Тут this вказує на новий створений об'єкт (u). Оператор new створює порожній об'єкт, робить його контекстом виклику і повертає його, якщо функція явно не повернула інший об'єкт.
call, apply і bind
function greet() {
console.log(this.name);
}
const user = { name: "Maria" };
greet.call(user); // "Maria"
greet.apply(user); // "Maria"
const boundGreet = greet.bind(user);
boundGreet(); // "Maria"| Метод | Що робить |
|---|---|
call(thisArg, arg1, arg2, ...) | викликає функцію з указаним this і аргументами через кому |
apply(thisArg, [args]) | те саме, але аргументи передаються масивом |
bind(thisArg) | повертає нову функцію із зафіксованим this, не викликаючи її одразу |
Стрілкові функції та підсумок
const obj = {
name: "Alice",
arrow: () => console.log(this.name),
regular() {
console.log(this.name);
},
};
obj.arrow(); // undefined, this це зовнішній контекст, не obj
obj.regular(); // "Alice"Важливо:
- Стрілкові функції не мають власного
this. - Вони «беруть»
thisіз зовнішнього лексичного оточення, тобто звідти, де їх створено. - Через це
call,applyіbindне змінюютьthisу стрілковій функції.
Підсумкова таблиця:
| Ситуація | Яким буде this |
|---|---|
| Звичайний виклик | window або undefined |
| Метод об'єкта | Сам об'єкт |
new | Новий екземпляр |
call, apply, bind | Вказаний вручну об'єкт |
| Стрілкова функція | Успадковує this ззовні |
Контекст функції це «власник виклику»: хто викликає функцію, той і стає
this.
Типові помилки
- Вважати, що
thisвизначається місцем оголошення. Ні, він визначається місцем і способом виклику. - Передавати метод як колбек без прив'язки.
setTimeout(user.sayHi, 100)втрачає контекст. Требаuser.sayHi.bind(user)або стрілкова обгортка() => user.sayHi(). - Робити метод об'єкта стрілковою функцією. Тоді
thisвказує не на об'єкт, а на зовнішнє оточення, іthis.nameдастьundefined. - Плутати
callіapply.callприймає аргументи через кому,applyприймає масив. - Очікувати, що
bindможна перевизначити. Прив'язану функцію не можна перепривʼязати повторнимbindабоcall: перша прив'язка перемагає. - Забувати про strict mode. У модулях і класах код завжди строгий, тому в звичайному виклику там буде
undefined, а неwindow.
Коротка відповідь
Для співбесідиКоротка відповідь допоможе вам впевнено відповідати на цю тему під час співбесіди.