У JavaScript кожна функція має this
посилання, яке автоматично створюється при оголошенні.
JavaScript this
дуже схожий на this
посилання в інших мовах, заснованих на класах, таких як Java або C # (JavaScript - це мова, що базується на прототипі, і не має поняття "клас"): він вказує на те, який об'єкт викликає функцію (цей об'єкт іноді називають як контекст ). В JavaScript, проте, посилання всередині функції можуть бути пов'язані з різними об'єктами в залежності від того, де функція викликається .this
Ось 5 основних правил this
прив’язки в JavaScript:
Правило 1
Коли функція викликається в глобальній області, this
посилання за замовчуванням прив'язується до глобального об'єкта ( window
у браузері або global
в Node.js). Наприклад:
function foo() { this.a = 2; } foo(); console.log(a); // 2
Примітка: Якщо ви оголосите foo()
наведену вище функцію в суворому режимі, тоді ви викличете цю функцію в глобальній області, this
буде undefined
і призначення this.a = 2
приведе до Uncaught TypeError
винятку.
Правило 2
Давайте розглянемо приклад нижче:
function foo() { this.a = 2; } const obj = { foo: foo }; obj.foo(); console.log(obj.a); // 2
Очевидно, що у наведеному вище фрагменті foo()
функція викликається з контекстом є obj
об’єктом, а this
посилання тепер пов’язане obj
. Отже, коли функція викликається із об’єктом контексту, this
посилання буде прив’язане до цього об’єкта.
Правило 3
.call
, .apply
і .bind
всі вони можуть бути використані на сайті виклику для явного прив’язки this
. Використання .bind(this)
- це те, що ви можете побачити у великій кількості компонентів React.
const foo = function() { console.log(this.bar) } foo.call({ bar: 1 }) // 1
Ось короткий приклад того, як кожен з них використовується для зв'язування this
:
.call()
:fn.call(thisObj, fnParam1, fnParam2)
.apply()
:fn.apply(thisObj, [fnParam1, fnParam2])
.bind()
:const newFn = fn.bind(thisObj, fnParam1, fnParam2)
Правило 4
function Point2D(x, y) { this.x = x; this.y = y; } const p1 = new Point2D(1, 2); console.log(p1.x); // 1 console.log(p1.y); // 2
Річ, яку ви повинні помітити, - це Point2D
функція, що викликається new
ключовим словом, а this
посилання прив’язується до p1
об’єкта. Отже, коли функція викликається за допомогою new
ключового слова, вона створить новий об’єкт, і this
посилання буде прив’язане до цього об’єкта.
Примітка: Коли ви викликаєте функцію з new
ключовим словом, ми також називаємо її як функцію конструктора .
Правило 5
JavaScript визначає значення this
під час виконання, виходячи з поточного контексту. Тому this
іноді може вказати на щось інше, ніж те, що ви очікуєте.
Розглянемо цей приклад класу Cat із викликаним методом makeSound()
, дотримуючись шаблону в Правилі 4 (вище) із функцією конструктора та new
ключовим словом.
const Cat = function(name, sound) { this.name = name; this.sound = sound; this.makeSound = function() { console.log( this.name + ' says: ' + this.sound ); }; } const kitty = new Cat('Fat Daddy', 'Mrrooowww'); kitty.makeSound(); // Fat Daddy says: Mrrooowww
Тепер спробуємо дати коту дорогу annoy()
людям, повторюючи його звук 100 разів, раз на півсекунди.
const Cat = function(name, sound) { this.name = name; this.sound = sound; this.makeSound = function() { console.log( this.name + ' says: ' + this.sound ); }; this.annoy = function() { let count = 0, max = 100; const t = setInterval(function() { this.makeSound(); // <-- this line fails with `this.makeSound is not a function` count++; if (count === max) { clearTimeout(t); } }, 500); }; } const kitty = new Cat('Fat Daddy', 'Mrrooowww'); kitty.annoy();
Це не працює, оскільки всередині setInterval
зворотного виклику ми створили новий контекст із глобальним масштабом, тому this
більше не вказуємо на наш екземпляр кошеня. Натомість у веб-браузері this
вказуватиме на об’єкт Window, який не має makeSound()
методу.
Кілька способів змусити це працювати:
- Перш ніж створювати новий контекст, призначте
this
локальну змінну з назвоюme
абоself
, або як завгодно, яку ви хочете викликати, і використовуйте цю змінну всередині зворотного виклику.
const Cat = function(name, sound) { this.name = name; this.sound = sound; this.makeSound = function() { console.log( this.name + ' says: ' + this.sound ); }; this.annoy = function() { let count = 0, max = 100; const self = this; const t = setInterval(function() { self.makeSound(); count++; if (count === max) { clearTimeout(t); } }, 500); }; } const kitty = new Cat('Fat Daddy', 'Mrrooowww'); kitty.annoy();
- За допомогою ES6 ви можете уникнути призначення
this
локальної змінної за допомогою функції стрілки, яка прив'язуєтьсяthis
до контексту оточуючого коду, де це визначено.
const Cat = function(name, sound) { this.name = name; this.sound = sound; this.makeSound = function() { console.log( this.name + ' says: ' + this.sound ); }; this.annoy = function() { let count = 0, max = 100; const t = setInterval(() => { this.makeSound(); count++; if (count === max) { clearTimeout(t); } }, 500); }; } const kitty = new Cat('Fat Daddy', 'Mrrooowww'); kitty.annoy();