Під час роботи з JavaScript однією з найпоширеніших причин непорозумінь є поведінка ключового слова this
. Це питання стає ще складнішим, коли порівнювати стрілкові функції (arrow functions) та звичайні функції. Розуміння того, як працює this
в обох типах функцій, є важливим для написання чистого і безпомилкового коду.
Давайте детальніше розглянемо особливості роботи this
, проаналізуємо приклади і роз'яснимо, як ця поведінка впливає на ваші практики програмування.
Ключова різниця: Лексичне vs. Динамічне прив'язування
Стрілкові функції прив'язують this
лексично
Стрілкові функції не мають власного this
. Натомість вони успадковують this
від оточуючого лексичного контексту — місця, де була визначена функція.
Це називається лексичне обмеження області видимості (lexical scoping).
Приклад:
const obj = {
value: 42,
arrowFunc: () => {
console.log(this.value); // `this` посилається на зовнішній (глобальний) контекст
},
regularFunc: function () {
console.log(this.value); // `this` посилається на `obj`
}
};
obj.arrowFunc(); // undefined (в строгому режимі)
obj.regularFunc(); // 42
- В стрілковій функції
this
не посилається наobj
, а на глобальний контекст (абоundefined
у строгому режимі). - У звичайній функції
this
динамічно прив'язується доobj
, коли вона викликається як метод.
Звичайні функції прив'язують this
динамічно
Звичайні функції мають власний this
, і його значення визначається під час виконання в залежності від того, як викликається функція:
- Як метод:
this
посилається на об'єкт, який є властивістю функції. - Як самостійна функція:
this
посилається на глобальний об'єкт (абоundefined
у строгому режимі). - У конструкторі:
this
посилається на новостворений об'єкт.
Приклад:
function regularFunc() {
console.log(this);
}
const obj = { regularFunc };
obj.regularFunc(); // `this` посилається на `obj`
regularFunc(); // `this` посилається на глобальний об'єкт або `undefined`
Стрілкові функції ідеально підходять для колбеків
Одна з основних причин існування стрілкових функцій — це спрощення колбек-функцій.
Звичайні функції часто вимагають ручного прив'язування this
, щоб забезпечити правильний контекст.
За допомогою стрілкової функції:
function Timer() {
this.seconds = 0;
setInterval(() => {
this.seconds++;
console.log(this.seconds); // Правильно виводить час
}, 1000);
}new Timer();
За допомогою звичайної функції (потрібне прив'язування):
function Timer() {
this.seconds = 0;
setInterval(function () {
this.seconds++; // `this` буде undefined без прив'язування
console.log(this.seconds);
}.bind(this), 1000);
}new Timer();
Стрілкові функції успадковують this
від контексту функції Timer
, усуваючи необхідність у .bind()
.
Прототип та поведінка конструктора
Стрілкові функції не можуть бути конструкторами
Стрілкові функції не мають властивості prototype
, що означає, що їх неможливо використовувати з ключовим словом new
для створення нових об'єктів.
Це відбувається через те, що стрілкові функції не мають свого власного this
.
Приклад:
const ArrowFunc = () => {};
function RegularFunc() {}
new RegularFunc(); // Працює
new ArrowFunc(); // TypeError: ArrowFunc is not a constructor
Наслідування через прототип
Звичайні функції можна використовувати для наслідування через прототип завдяки їх властивості prototype
, але стрілкові функції цього не можуть.
Практичні випадки використання
1. Прослуховувачі подій (Event Listeners)
Коли ви використовуєте стрілкові функції як обробники подій, потрібно бути обережними, оскільки this
не посилається на елемент, який ініціював подію.
Приклад:
const button = document.querySelector('button');
button.addEventListener('click', () => {
console.log(this); // `this` посилається на глобальний контекст, а не на кнопку
});
button.addEventListener('click', function () {
console.log(this); // `this` посилається на кнопку
});
2.
Методи об'єктів
Уникайте використання стрілкових функцій для методів об'єктів, якщо вам потрібно, щоб this
посилалося на сам об'єкт.
Приклад:
const obj = {
name: 'Example',
getName: () => {
console.log(this.name); // undefined
},
getNameRegular: function () {
console.log(this.name); // 'Example'
}
};
obj.getName(); // undefined
obj.getNameRegular(); // 'Example'
Підсумок: Коли використовувати кожну
Висновок
Стрілкові функції — потужний інструмент в JavaScript, який спрощує код шляхом лексичного зв'язування this
. Однак їхня поведінка кардинально відрізняється від звичайних функцій, особливо коли йдеться про прототипи, конструктори або динамічні контексти.
Освоєння різниці між цими двома типами функцій зробить ваш код чистішим і допоможе уникнути поширених помилок.
Використовуйте стрілкові функції там, де простота та лексичне зв'язування (lexical scoping
) найбільш корисні, і обирайте звичайні функції, коли вам потрібна динамічна поведінка this
або наслідування на основі прототипів.
Розуміючи ці відмінності, ви відкриєте для себе глибший рівень майстерності в JavaScript і писатимете код, який буде не лише надійним, а й легким для підтримки.
Перекладено з: Understanding this in JavaScript: Arrow Functions vs. Regular Functions