@eonae

JavaScript. Как правильно подписаться на событие в конструкторе класса?

Доброго времени суток!

JS осваиваю недавно, раньше программировал на C#. Не могу разобраться, что я делаю не так. Убил несколько часов, прочитал про: контекст, замыкания, стрелочные функции, bind, call и прочее... но так и не понял.

Есть класс EventEmitter, "наследуясь" от которого можно получить возможность генерировать кастомные "события". Есть класс Model, генерирующая "change" при изменении данных и есть некий компонент (класс Component), который получает ссылку на модель.

Я хочу, подписать создаваемый компонент на событие модели - прямо в конструторе. Но как не извращаюсь, не могу получить нужно значение this в обработчике события. Всё время this ссылается только на компонент, созданный последним (((

Приводимый ниже код - базовый вариант (вокруг него уже поставлено множество экспериментов):

spoiler
// Базовый класс, позволяющий подписывать на события.

class EventEmitter {

  constructor() {
    this.handlers = {};
  }
  
  on(eventName, handler) {

      if (eventName in this.handlers) {
          this.handlers[eventName].push(handler);
      } else {
          this.handlers[eventName] = [ handler ];
      }
  };

  emit(eventName, args) {
      if (eventName in this.handlers) {
          for (var handler of this.handlers[eventName]) {
              setTimeout(() => { handler(); }, 0);
          }
      }
  }
}

// Модель данных триггерит событие change, при изменении.

class Model extends EventEmitter {

  constructor() {
    super();
  }

  set(entries) {
    
    const keys = Object.keys(entries);
    keys.forEach(key => {
      this[key] = entries[key];
    });
    this.emit('change', keys);
  }

}

// Класс компонента. При создании подписывается на изменение модели.

class Component extends EventEmitter {

  constructor(settings, model) {
    super();
    this.isActive = false;
    this.model = model;
    this.uses = settings.uses;

    if (settings.children) {
      this.children = {};
      for (let child of Object.entries(settings.children)) {
        var key = child[0], componentSettings = child[1];

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

        this.children[key] = new Component(componentSettings, this.model); 
      }
    }

    model.on('change', () => {
      // Здесь будет более сложная логика, но пока нужно разобраться с this.
      console.log(settings);
      console.log(this);
    });
  }
}

var model = new Model();

var view = new Component({
  
  template: 'gameLayout',
  children: {

    player: {
      template: 'player',
      slot: '#player',
      uses: [ 'player' ]
    },

    menu: {
      template: 'menu',
      slot: '#menu'
    },

    question: {
      template: 'question',
      slot: '#question',
      uses: [ 'question' ]
    }
  }
  
}, model);

model.set({ property: 'value' });


В общем, в очередной раз понимаю, что ничего не понимаю в JS..

Если кто-то сможет объяснить, в чём я ошибаюсь, или хотя просто показать, как правильно, буду очень благодарен.

С уважением, Сергей.

P. S. Особенно раздражает момент, что когда я просматриваю в консоли model.handlers - там показано, что каждый handler в замыкании имеет свой объект settings... но выводится всё равно одно и то же. В общем, упёрся (
  • Вопрос задан
  • 293 просмотра
Пригласить эксперта
Ответы на вопрос 1
@eonae Автор вопроса
Всех благодарю и прошу прощения за беспокойство!

Ошибка найдена, и дело вовсе не в контексте, а в ненужном замыкании!

В методе EventEmitter.emit в цикле стоит var, а не let - вот и вся проблема. Всё-таки с контекстом я правильно разобрался, а вот аккуратность надо подтягивать!
Ответ написан
Комментировать
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Похожие вопросы