Есть ли в JavaScript лучший или распространенный способ использования членов класса в качестве обработчиков событий?
Рассмотрим следующий простой пример:
<head>
<script language = "javascript" type = "text/javascript">
ClickCounter = function(buttonId) {
this._clickCount = 0;
document.getElementById(buttonId).onclick = this.buttonClicked;
}
ClickCounter.prototype = {
buttonClicked: function() {
this._clickCount++;
alert('the button was clicked ' + this._clickCount + ' times');
}
}
</script>
</head>
<body>
<input type = "button" id = "btn1" value = "Click me" />
<script language = "javascript" type = "text/javascript">
var btn1counter = new ClickCounter('btn1');
</script>
</body>
Вызывается обработчик события buttonClicked, но член _clickCount недоступен или это указывает на какой-то другой объект.
Есть какие-нибудь полезные советы / статьи / ресурсы по такого рода проблемам?



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


ClickCounter = function(buttonId) {
this._clickCount = 0;
var that = this;
document.getElementById(buttonId).onclick = function(){ that.buttonClicked() };
}
ClickCounter.prototype = {
buttonClicked: function() {
this._clickCount++;
alert('the button was clicked ' + this._clickCount + ' times');
}
}
ИЗМЕНИТЬ почти 10 лет спустя, с ES6, стрелочными функциями и свойства класса
class ClickCounter {
count = 0;
constructor( buttonId ){
document.getElementById(buttonId)
.addEventListener( "click", this.buttonClicked );
}
buttonClicked = e => {
this.count += 1;
console.info(`clicked ${this.count} times`);
}
}
https://codepen.io/anon/pen/zaYvqq
Это хороший пример, но этот ответ может быть лучше с некоторым объяснением того, почему это хорошее решение для новичков здесь.
Редактирование ES2015 работает через Babel, но синтаксис общедоступных полей пока не разрешен во всех браузерах (даже сейчас, в 2019 году).
не совсем сработало в моем приложении. Базовый класс создается фабрикой, которая также устанавливает прослушиватель событий. Переменная this при срабатывании события принадлежит фабрике. Попытка выяснить, как addEventListener знает, что нужно использовать при вызове обработчика событий ...
изменил метод обработчика с clicked () {...} на clicked = () => {...}, и он работает. Поскольку анонимные функции используют this по-разному, насколько я понимаю?
>> Потому что анонимные функции по-разному используют this? Это потому, что лямбда захватывает указатель this при его определении. Таким образом, обработчик в этом случае является объектом функции с состоянием, а не функцией, как обычный метод.
Функция, прикрепленная непосредственно к свойству onclick, будет иметь свойство this контекста выполнения, указывающее на элемент.
Когда вам нужно, чтобы событие элемента запускалось для определенного экземпляра объекта (как делегат в .NET), вам понадобится закрытие: -
function MyClass() {this.count = 0;}
MyClass.prototype.onclickHandler = function(target)
{
// use target when you need values from the object that had the handler attached
this.count++;
}
MyClass.prototype.attachOnclick = function(elem)
{
var self = this;
elem.onclick = function() {self.onclickHandler(this); }
elem = null; //prevents memleak
}
var o = new MyClass();
o.attachOnclick(document.getElementById('divThing'))
Я не знаю, почему здесь еще не упомянули Function.prototype.bind. Так что я просто оставлю это здесь;)
ClickCounter = function(buttonId) {
this._clickCount = 0;
document.getElementById(buttonId).onclick = this.buttonClicked.bind(this);
}
ClickCounter.prototype = {
buttonClicked: function() {
this._clickCount++;
alert('the button was clicked ' + this._clickCount + ' times');
}
}
Действительно чистое и простое решение! +1
это не работает. "this.buttonClicked не определено"
@FabioDelarias должно работать, проверьте пример. codepen.io/anon/pen/QzwRKE?editors=1010
Отличное решение, и, вероятно, именно так оно и должно было быть сделано.
Вы можете использовать синтаксис с жирной стрелкой, который привязан к лексической области видимости функции.
function doIt() {
this.f = () => {
console.info("f called ok");
this.g();
}
this.g = () => {
console.info("g called ok");
}
}
После этого вы можете попробовать
var n = new doIt();
setTimeout(n.f,1000);
Вы можете попробовать его на Вавилон или, если ваш браузер поддерживает ES6, на jsFiddle.
К сожалению, синтаксис класса ES6, похоже, не позволяет создавать функцию, лексически привязанную к этому. Я лично думаю, что это могло бы быть так. Обновлено: Кажется, есть экспериментальная функция ES7, позволяющая это.
Мне нравится использовать функции unnamed, просто реализовал класс навигации, который с этим справляется правильно:
this.navToggle.addEventListener('click', () => this.toggleNav() );
тогда this.toggleNav() может быть просто функцией в Class.
Я знаю, что раньше вызывал именованную функцию, но это может быть любой код, который вы вставляете между ними, например:
this.navToggle.addEventListener('click', () => { [any code] } );
Из-за arrow вы передаете этот экземпляр и можете использовать его там.
У Павла было немного другое соглашение, но я думаю, что лучше использовать functions, потому что соглашения об именах для Classes и Methods в нем - способ пойти :-)
Спасибо за ваше решение. Я также пытаюсь понять более широкую картину - шаблоны, практики и т. д. Есть какие-нибудь хорошие статьи, о которых вы знаете?