Потеря контекста (this
) происходит, когда значение this
внутри функции не ссылается на ожидаемый объект. Это может произойти, если функция вызывается в контексте, который не является объектом, или если функция передается как обычная функция, а не как метод объекта.
Существует несколько способов, которые можно использовать для избежания потери контекста:
- Использование стрелочных функций - стрелочные функции привязывают значение
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)
- Использование метода
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"
- Использование метода
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"
- Использование переменной для хранения контекста - можно использовать переменную для хранения контекста, чтобы избежать потери контекста при передаче функции в качестве аргумента.
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
в ожидаемом контексте.
- Потеря контекста внутри
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() // Вася