Розуміння «це» ключового слова JavaScript

Яке значення має ключове слово this у JavaScript? Як його можна ефективно застосовувати у ваших JavaScript програмах? Ці питання часто виникають як у новачків, так і у досвідчених розробників, коли справа доходить до розуміння ключового слова this.

Якщо ви належите до тих, хто прагне розібратися з цим ключовим словом, ця стаття стане вашим путівником. Ми дослідимо, як this поводиться в різних ситуаціях, та розглянемо типові помилки, яких слід уникати, щоб запобігти непорозумінням та багам у вашому коді.

this у глобальній області видимості

В глобальному контексті, за межами будь-якої функції, this повертає об’єкт window. Глобальний контекст означає, що this використовується поза межами будь-якої функції.

if(true) {
  console.log(this) 
}

let i = 2 while(i < 10) { console.log(this) i++ }

Виконання наведеного вище коду покаже об’єкт window.

this всередині функцій (методів)

Коли this використовується всередині функції, воно посилається на об’єкт, з яким пов’язана ця функція. Винятком є ситуація, коли this використовується у звичайній, незв’язаній функції, тоді воно повертає об’єкт window. Розглянемо декілька прикладів.

У наступному прикладі функція sayName є частиною об’єкта me (тобто це метод). В таких випадках this вказує на об’єкт, що містить функцію.

function sayName() {
  return `Мене звати ${this.name}`
}

const me = { name: "Кінгслі", sayName: sayName }

console.log(me.sayName())

Тут this є об’єктом me, тому this.name в методі sayName еквівалентно me.name.

Інший спосіб це зрозуміти: все, що знаходиться зліва від виклику функції, буде значенням this. Це означає, що функцію sayName можна використовувати в різних об’єктах, і в кожному випадку this посилатиметься на інший контекст.

Як згадувалося раніше, this повертає об’єкт window, коли воно використовується всередині самостійної функції. Це відбувається тому, що за замовчуванням окрема функція пов’язана з об’єктом window:

function talk() {
  return this
}

talk()

Виклик talk() еквівалентний window.talk(), і все, що знаходиться ліворуч від функції, автоматично стає значенням this.

Варто зазначити, що поведінка ключового слова this у функції змінюється у строгому режимі JavaScript (воно повертає undefined). Це слід пам’ятати при використанні бібліотек, які застосовують строгий режим (наприклад, React).

Використання this з Function.bind()

Іноді неможливо просто додати функцію до об’єкта як метод (як у попередньому розділі).

Об’єкт може бути вам не підвладний, або ви можете отримувати його з бібліотеки. Об’єкт є незмінним, і його не можна модифікувати. У таких ситуаціях можна викликати функцію окремо від об’єкта, використовуючи метод Function.bind().

У наведеному прикладі функція sayName не є методом об’єкта me, але ви все одно пов’язуєте її за допомогою bind():

function sayName() {
  return `Мене звати ${this.name}`
}

const me = { name: "Кінгслі" }

const meTalk = sayName.bind(me)

meTalk()

Будь-який об’єкт, переданий у bind(), буде використовуватись як значення this у виклику функції.

Таким чином, bind() дозволяє використовувати будь-яку функцію, передаючи їй новий контекст (об’єкт). Цей об’єкт перезапише значення this всередині функції.

Використання this з Function.call()

А якщо потрібно не повертати нову функцію, а просто викликати наявну функцію, попередньо пов’язавши її з контекстом? Для цього існує метод call():

function sayName() {
  return `Мене звати ${this.name}`
}

const me = { name: "Кінгслі" }

sayName.call(me)

Метод call() виконує функцію негайно, замість того, щоб повертати іншу функцію.

Якщо функції потрібні параметри, їх можна передати через метод call(). У наступному прикладі передається параметр мови функції sayName(), що дає можливість умовно повертати різні повідомлення:

function sayName(lang) {
  if (lang === "en") {
    return `Мене звати ${this.name}`
  } else if (lang === "it") {
    return `Я ${this.name}`
  }
}

const me = { name: "Кінгслі" }

sayName.call(me, 'en') sayName.call(me, 'it')

Як бачите, можна передати будь-який параметр у функцію як другий аргумент методу call(). Можна передати скільки завгодно параметрів.

Метод apply() дуже схожий на call() та bind(). Єдина відмінність полягає в тому, що множинні аргументи передаються через кому у call(), тоді як в apply() вони передаються у вигляді масиву.

Підсумовуючи, bind(), call() та apply() дозволяють викликати функції з іншим об’єктом, не маючи між ними прямого зв’язку (функція не є методом об’єкта).

this всередині функцій-конструкторів

Якщо викликати функцію з ключовим словом new, створюється об’єкт this, який потім повертається:

function person(name){
  this.name = name
}

const me = new person("Кінгслі") const her = new person("Сара") const him = new person("Джейк")

me.name her.name him.name

У наведеному вище коді створено три різні об’єкти за допомогою однієї функції. Ключове слово new автоматично створює зв’язок між створюваним об’єктом і ключовим словом this у функції.

this всередині функцій зворотного виклику

Функції зворотного виклику відрізняються від звичайних. Функції зворотного виклику передаються іншій функції як аргумент і викликаються після завершення виконання основної.

Ключове слово this вказує на інший контекст, коли воно використовується у функціях зворотного виклику:

function person(name){
  this.name = name
  setTimeout(function() {
    console.log(this)
  }, 1000)
}

const me = new person("Кінгслі")

Через секунду після виклику функції-конструктора person і створення об’єкта me, об’єкт window буде виведений в консоль як значення this. Тобто, при використанні у функції зворотного виклику, this вказує на об’єкт window, а не на “сконструйований” об’єкт.

Є два способи це виправити. Перший використовує bind() для зв’язування функції person з новоствореним об’єктом:

function person(name){
  this.name = name
  setTimeout(function() {
    console.log(this)
  }.bind(this), 1000)
}

const me = new person("Кінгслі")

З цією модифікацією this у функції зворотного виклику посилатиметься на те саме this, що і у функції-конструкторі (об’єкт me).

Другий спосіб розв’язання цієї проблеми у функціях зворотного виклику – використовувати стрілкові функції.

this всередині стрілкових функцій

Стрілкові функції відрізняються від звичайних. Можна зробити функцію зворотного виклику стрілковою. Зі стрілковими функціями bind() більше не потрібен, тому що вони автоматично пов’язуються з щойно створеним об’єктом:

function person(name){
  this.name = name
  setTimeout(() => {
    console.log(this)
  }, 1000)
}

const me = new person("Кінгслі")

Дізнайтеся більше про JavaScript

Ви дізналися все про ключове слово this та його значення в різних контекстах JavaScript. Якщо ви початківець у JavaScript, вам буде дуже корисно вивчити всі основи JavaScript та його роботу.