Методы класса как обработчики событий в JavaScript?

Есть ли в 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 недоступен или это указывает на какой-то другой объект.

Есть какие-нибудь полезные советы / статьи / ресурсы по такого рода проблемам?

Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
В настоящее время производительность загрузки веб-сайта имеет решающее значение не только для удобства пользователей, но и для ранжирования в...
Безумие обратных вызовов в javascript [JS]
Безумие обратных вызовов в javascript [JS]
Здравствуйте! Юный падаван 🚀. Присоединяйся ко мне, чтобы разобраться в одной из самых запутанных концепций, когда вы начинаете изучать мир...
Система управления парковками с использованием HTML, CSS и JavaScript
Система управления парковками с использованием HTML, CSS и JavaScript
Веб-сайт по управлению парковками был создан с использованием HTML, CSS и JavaScript. Это простой сайт, ничего вычурного. Основная цель -...
JavaScript Вопросы с множественным выбором и ответы
JavaScript Вопросы с множественным выбором и ответы
Если вы ищете платформу, которая предоставляет вам бесплатный тест JavaScript MCQ (Multiple Choice Questions With Answers) для оценки ваших знаний,...
27
0
28 114
5
Перейти к ответу Данный вопрос помечен как решенный

Ответы 5

Ответ принят как подходящий
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

Спасибо за ваше решение. Я также пытаюсь понять более широкую картину - шаблоны, практики и т. д. Есть какие-нибудь хорошие статьи, о которых вы знаете?

JacobE 23.10.2008 13:36

Это хороший пример, но этот ответ может быть лучше с некоторым объяснением того, почему это хорошее решение для новичков здесь.

Sam Murray-Sutton 05.12.2008 17:31

Редактирование ES2015 работает через Babel, но синтаксис общедоступных полей пока не разрешен во всех браузерах (даже сейчас, в 2019 году).

Pointy 23.03.2019 16:16

не совсем сработало в моем приложении. Базовый класс создается фабрикой, которая также устанавливает прослушиватель событий. Переменная this при срабатывании события принадлежит фабрике. Попытка выяснить, как addEventListener знает, что нужно использовать при вызове обработчика событий ...

user10469346 01.06.2019 22:11

изменил метод обработчика с clicked () {...} на clicked = () => {...}, и он работает. Поскольку анонимные функции используют this по-разному, насколько я понимаю?

user10469346 01.06.2019 22:35

>> Потому что анонимные функции по-разному используют this? Это потому, что лямбда захватывает указатель this при его определении. Таким образом, обработчик в этом случае является объектом функции с состоянием, а не функцией, как обычный метод.

QBziZ 01.03.2021 12:10

Функция, прикрепленная непосредственно к свойству 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

Michael IV 06.05.2018 21:08

это не работает. "this.buttonClicked не определено"

Fabio Delarias 11.12.2018 02:43

@FabioDelarias должно работать, проверьте пример. codepen.io/anon/pen/QzwRKE?editors=1010

kucherenkovova 11.12.2018 13:23

Отличное решение, и, вероятно, именно так оно и должно было быть сделано.

JimmyB 17.10.2019 13:13

Вы можете использовать синтаксис с жирной стрелкой, который привязан к лексической области видимости функции.

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 в нем - способ пойти :-)

Другие вопросы по теме