Потеря контекста (this) происходит, когда значение this внутри функции не ссылается на ожидаемый объект. Это может произойти, если функция вызывается в контексте, который не является объектом, или если функция передается как обычная функция, а не как метод объекта.

Существует несколько способов, которые можно использовать для избежания потери контекста:

  1. Использование стрелочных функций - стрелочные функции привязывают значение this к контексту внешней функции, т.е. контексту, в котором они были определены. При использовании стрелочных функций не происходит потери контекста.
const obj = {
  name: "John",
  greet: function () {
    setTimeout(() => {
      console.log(`Hello, my name is ${this.name}`)
    }, 1000)
  },
}
 
obj.greet() // "Hello, my name is John" (after 1 second delay)
  1. Использование метода bind() - метод bind() создает новую функцию, которая привязывает значение this к определенному объекту. Это позволяет сохранить контекст функции, когда она вызывается в другом контексте.
const obj = {
  name: "John",
  greet: function () {
    console.log(`Hello, my name is ${this.name}`)
  },
}
 
const otherObj = {
  name: "Jane",
}
 
const boundGreet = obj.greet.bind(otherObj)
boundGreet() // "Hello, my name is Jane"
  1. Использование метода call() или apply() - методы call() и apply() вызывают функцию с определенным значением this. Разница между этими методами заключается в том, как передаются аргументы функции.
const obj = {
  name: "John",
  greet: function () {
    console.log(`Hello, my name is ${this.name}`)
  },
}
 
const otherObj = {
  name: "Jane",
}
 
obj.greet.call(otherObj) // "Hello, my name is Jane"
obj.greet.apply(otherObj) // "Hello, my name is Jane"
  1. Использование переменной для хранения контекста - можно использовать переменную для хранения контекста, чтобы избежать потери контекста при передаче функции в качестве аргумента.
const obj = {
  name: "John",
  greet: function () {
    console.log(`Hello, my name is ${this.name}`)
  },
}
 
const otherObj = {
  name: "Jane",
}
 
const context = obj
context.greet() // "Hello, my name is John"
 
someFunction(context.greet) // передача метода с сохраненным контекстом

Эти способы помогают избежать потери контекста в JavaScript и сохранить значение this в ожидаемом контексте.

  1. Потеря контекста внутри setTimeout. Как только метод передаётся отдельно от объекта – this теряется.
let user = {
  firstName: "Вася",
  sayHi() {
    alert(`Привет, ${this.firstName}!`)
  },
}
 
setTimeout(user.sayHi, 1000) // Привет, undefined!

При запуске этого кода мы видим, что вызов this.firstName возвращает не «Вася», а undefined!

Это произошло потому, что setTimeout получил функцию sayHi отдельно от объекта user (именно здесь функция и потеряла контекст).

Решение 1: Сделать функцию-обёртку

let user = {
  firstName: "Вася",
  sayHi() {
    alert(`Привет, ${this.firstName}!`)
  },
}
 
setTimeout(function () {
  user.sayHi() // Привет, Вася!
}, 1000)

Что произойдёт, если до момента срабатывания setTimeout (ведь задержка составляет целую секунду!) в переменную user будет записано другое значение? Тогда вызов неожиданно будет совсем не тот!

Решение 2: Привязать контекст с помощью bind. Без возникновения уязвимости, как в Решении 1.

let user = {
  firstName: "Вася",
}
 
function func() {
  alert(this.firstName)
}
 
let funcUser = func.bind(user)
funcUser() // Вася

Назад