У меня проблема с созданным мной объектом, который выглядит примерно так:
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», что я делаю не так?
В вашем комментарии говорится, что ошибка связана с вызовом SomethingElse, но разве это не связано с вызовом DoSomething?
Замыкания на самом деле не имеют ничего общего с этим примером, значение, если this не исходит из замыкания здесь.
Отвечает ли это на ваш вопрос? Как получить доступ к правильному `this` внутри обратного вызова?



![Безумие обратных вызовов в javascript [JS]](https://i.imgur.com/WsjO6zJb.png)


КОГДА функция вызывается, «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, которая была в области видимости, когда функция была определена.
мне потребовалось некоторое время, чтобы получить это - причина в том, что row.attachEvent прикрепляет событие к строке, и когда это событие срабатывает, строка является той, которая вызывает функцию (она, вероятно, делает что-то вроде f.apply (this, args )). я прав?
Это обычная проблема с закрытием. Чтобы решить эту проблему, попробуйте что-то вроде этого:
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();
}
}
К вашему сведению, «self» - очень опасное имя переменной в контексте Javascript. В зависимости от вашей области действия вы можете фактически получить ссылку на объект document.window.
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);
}
}
}
Вы уверены, что "that = this;" действителен в случае определения его внутри литерала объекта? @svinto
@GnrlBzik: Хороший улов! Перед ним должен быть var.
чувак, это объект, он ожидает пары ключ: значение. запустите этот код против jshint.com
Похоже я очень устал что ли, извините за глупость.
бывает :) я делаю это постоянно; )
Проблема с вашим кодом здесь, в следующей строке кода вы определили анонимную функцию и передали ее в качестве обработчика события 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.
}
}
вы упомянули, что ошибка находится в this.SomthingElse (), но ваш код не показывает ее. Если вы действительно получаете ошибку в этой строке кода, возможно, вы используете метод DoSomthing где-то еще в качестве обработчика событий.
Проблема заключается в том, как this управляется в JS, что может быть быстро, особенно когда у вас асинхронно вызывается обратный вызов (и когда создается закрытие, привязанное к этому).
Когда вызывается метод AddChildRowEvents, this хорошо ссылается на переменную myObject. Но цель этого метода - поставить обработчик клика. Так что событие будет запущено когда-нибудь в будущем. В то время какой объект действительно вызовет событие? Фактически, это DOM element, на который пользователь будет нажимать. Таким образом, this будет ссылаться на эту DOM element, а не на переменную myObject.
Для более современного решения, чем представленные, можно использовать bind method или arrow function.
let handler = {
addEventHandler: function(row) {
console.info("this", this)
row.addEventListener("click", () => {
console.info("this", this)
this.doSomethingElse()
})
},
doSomethingElse: function() {
console.info("something else")
}
}
var div = document.querySelector("div")
handler.addEventHandler(div)<div>one</div>В arrow function (которые доступны с ES2015) контекст this находится не в контексте выполнения, а в лексическом контексте. Разница между ними в том, что лексический контекст обнаруживается во время сборки, а не во время выполнения, поэтому лексический контекст легче отслеживать. Здесь, внутри arrow function, this ссылается на тот же this внешней функции (то есть addEventHandler), так что ссылка на myObject.
Дополнительные сведения о свойствах функции жирной стрелки по сравнению с обычными функциями: https://dmitripavlutin.com/6-ways-to-declare-javascript-functions/
let handler = {
addEventHandler: function(row) {
console.info("this", this)
row.addEventListener("click", function() {
console.info("this", this)
this.doSomethingElse()
}.bind(this))
},
doSomethingElse: function() {
console.info("something else")
}
}
var div = document.querySelector("div")
handler.addEventHandler(div)<div>one</div>На этот раз, когда функция обратного вызова передается в addEventListener, можно связать контекст this с элементом this во внешней функции addEventHandler.
.bind () работает медленно (ссылка: sitepoint.com/…)
1) глупый вопрос, а есть ли у вашего объекта метод SomethingElse? 2) попробуйте выполнить "function () {this.DoSomething (this);}" в закрытии, и пусть DoSomething принимает либо 0 аргументов, либо 1 аргумент - посмотрите, что произойдет, и опубликуйте свои результаты.