Закрытие Javascript и 'this'
У меня есть проблема с созданным мной объектом, который выглядит примерно так:
var myObject = {
AddChildRowEvents: function(row, p2) {
if(document.attachEvent) {
row.attachEvent('onclick', function(){this.DoSomething();});
} else {
row.addEventListener('click', function(){this.DoSomething();}, false);
}
},
DoSomething: function() {
this.SomethingElse(); //<-- Error here, object 'this' does not support this method.
}
}
Проблема в том, что когда я внутри функции "DoSomething", 'this' не ссылается на "myObject", что я делаю неправильно?
Ответы
Ответ 1
Когда функция вызывается, "this" относится к строке. Если вы хотите иметь объект, вы можете сделать это примерно так:
]
AddChildRowEvents: function(row, p2) {
var theObj = this;
if(document.attachEvent) {
row.attachEvent('onclick', function(){theObj.DoSomething();});
} else {
row.addEventListener('click', function(){theObj.DoSomething();}, false);
}
},
Когда функция вызывается, она имеет доступ к переменной theOBj, которая была в области видимости, когда функция была определена.
Ответ 2
this
всегда ссылается на внутреннюю функцию, если у вас есть вложенные функции, вам нужно создать другую переменную и указать на this
.
var myObject = {
AddChildRowEvents: function(row, p2) {
var that = this;
if(document.attachEvent) {
row.attachEvent('onclick', function(){that.DoSomething();});
} else {
row.addEventListener('click', function(){that.DoSomething();}, false);
}
}
}
Ответ 3
Это обычная проблема с закрытием. Чтобы решить эту проблему, попробуйте что-то вроде этого:
var myObject = {
AddChildRowEvents: function(row, p2) {
var self = this;
if(document.attachEvent) {
row.attachEvent('onclick', function(){this.DoSomething(self);});
} else {
row.addEventListener('click', function(){this.DoSomething(self);}, false);
}
},
DoSomething: function(self) {
self.SomethingElse();
}
}
Ответ 4
Проблема с вашим кодом здесь, в следующей строке кода вы определили анонимную функцию и передали ее как обработчик события для события onClick в следующих строках:
row.attachEvent('onclick', function(){this.DoSomething();});
и
row.addEventListener('click', function(){this.DoSomething();}, false);
когда событие onclick поднято и вызывает анонимную функцию, которую вы передали, этот контекст ссылается на объект, который был поднят, но не myObject.
потому что в этот момент исполняемый контекст находится в контексте обработчика события.
Решение: вы можете сделать трюк, о котором сказал Майк Кантор, или используя jQuery, использовать метод прокси, чтобы определить этот контекст в анонимной функции.
Итак, ваш код будет таким:
var myObject = {
AddChildRowEvents: function(row, p2) {
if(document.attachEvent) {
row.attachEvent('onclick', $.proxy(function () {
this.DoSomething();
}, this));
} else {
row.addEventListener('click', $.proxy(function () {
this.DoSomething();
},this), false);
}
},
DoSomething: function() {
this.SomethingElse(); //<-- Error here, object 'this' does not support this method.
}
}
вы упомянули, что ошибка в этом. SomthingElse(), но ваш код не показывает его. Если вы действительно получаете ошибку в этой строке кода, возможно, вы используете метод DoSomthing где-то еще как обработчик событий.
Ответ 5
Проблема связана с тем, как this
управляется в JS, что может быть быстро, особенно когда вы вызываете асинхронно обратный вызов (и когда создается ограничение, привязанное к этому).
Когда вызывается метод AddChildRowEvents, this
хорошо ссылается на переменную myObject
. Но целью этого метода является установка обработчика щелчка. Таким образом, событие будет запущено в будущем. В то время какой объект действительно инициирует событие? Фактически, это DOM element
на который пользователь щелкнет. Таким образом, this
будет ссылка на этот DOM element
а не на переменную myObject
.
Для более современного решения, чем представленные, можно использовать bind method
или arrow function
.
1. Решение со стрелкой
let handler = {
addEventHandler: function(row) {
console.log("this", this)
row.addEventListener("click", () => {
console.log("this", this)
this.doSomethingElse()
})
},
doSomethingElse: function() {
console.log("something else")
}
}
var div = document.querySelector("div")
handler.addEventHandler(div)
<div>one</div>