Метод может быть извлечен из объекта в отдельную переменную (например const TEST = myObject.myMethod). Когда метод TEST() вызывается отделенным от исходного объекта, можно предположить, что this - это объект myObject, для которого был определен метод.
Однако, если метод вызывается отделенным от объекта (т.е. как TEST()), то происходит вызов функции, где this - это глобальный объект window или undefined в строгом режиме (см. 2.1 и 2.2).
var myObject = {
myMethod: function(){
console.log(this)
}
}
const TEST = myObject.myMethod;
myObject.myMethod() // => myObject
TEST() // => window
Только связанная функция myObject.myMethod.bind(myObject) (использование .bind() см. в главе 6) исправляет контекст, привязывая this к объекту, которому принадлежит метод.
В следующем примере определяется конструктор Pet и создается его экземпляр - myDog. Затем setTimout() через 1 секунду отобразит информацию об объекте myDog:
function Pet(type, legs) {
this.type = type;
this.legs = legs;
this.info = function() {
console.log(this === myDog); // => false
console.log('Животное -', this.type);
console.log('Количество лап -', this.legs);
}
}
const myDog = new Pet('Собака', 4);
myDog.info(); // => Животное - Собака | Количество лап - 4
setTimeout(myDog.info, 1000); // => Животное - undefined | Количество лап - undefined
На первый взгляд кажется, что setTimeout(myDog.info, 1000) вызовет myDog.info(), который покажет информацию об объекте myDog.
К сожалению, метод отделяется от своего объекта при передаче его в качестве параметра. Следующие случаи эквивалентны:
setTimout(myDog.info);
// эквивалентны
const extractedInfo = myDog.info;
setTimout(extractedInfo);
Когда отделенный метод info вызывается как функция, this становится глобальным объектом (или undefined), но не объектом myDog. Таким образом, информация об объекте не отображается корректно.
Функция связывается с объектом с помощью метода .bind() (см. главу 6). Если отделенный метод связать с объектом myDog, проблема отображения контекста будет решена:
function Pet(type, legs) {
this.type = type;
this.legs = legs;
this.info = function() {
console.log(this === myDog); // => true
console.log('Животное -', this.type);
console.log('Количество лап -', this.legs);
}
}
const myDog = new Pet('Собака', 4);
// Создадим связанную функцию
const boundInfo = myDog.info.bind(myDog);
setTimeout(boundInfo, 1000); // => Животное - Собака | Количество лап - 4
myDog.info.bind(myDog) возвращает новую функцию, которая выполняется точно так же, как info, но с this как у myDog даже при вызове функции.
Альтернативное решение - определить метод info() как стрелочную функцию, которая связывает this лексически:
function Pet(type, legs) {
this.type = type;
this.legs = legs;
this.info = () => {
console.log(this === myDog); // => true
console.log('Животное -', this.type);
console.log('Количество лап -', this.legs);
}
}
const myDog = new Pet('Собака', 4);
setTimeout(myDog.info, 1000); // => Животное - Собака | Количество лап - 4
Если вы хотите использовать классы и связать this с экземпляром класса в своем методе, используйте стрелочную функцию как свойство класса:
class Pet {
constructor(type, legs) {
this.type = type;
this.legs = legs;
}
info = () => {
console.log(this === myDog); // => true
console.log('Животное -', this.type);
console.log('Количество лап -', this.legs);
}
}
const myDog = new Pet('Собака', 4);
setTimeout(myDog.info, 1000); // => Животное - Собака | Количество лап - 4