Потеря контекста (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() // Вася