Повне керівництво з цього в JavaScript

У 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()методу.

Кілька способів змусити це працювати:

  1. Перш ніж створювати новий контекст, призначте 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();
  1. За допомогою 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();