首页 > 解决方案 > JavaScript 中的高阶函数并在 EventEmitter 的类中引用“this”关键字

问题描述

我已经在这个问题上停留了两天,并且在其他堆栈溢出帖子上没有骰子。我真的可以使用一些帮助。

我的目标是创建一个允许三个功能的事件发射器。订阅、取消订阅和 Emit - 其中,emit 将传递的参数发送到所有订阅函数。

我遇到的问题是我似乎无法将一个函数传递给订阅。我必须传递一个匿名函数来调用另一个函数。 subscribe(() => entities[1].eventEmitter.emit())subscribe(entities[1].eventEmitter.emit)。我似乎不明白这里的实际区别是什么,以及为什么他们会引用不同的“this”值。我认为第二个示例是第一个示例的简化版本?任何人都可以解释为什么两个版本引用不同的“this”值?

好吧,这一切都很好,因为传入匿名函数可以工作,但是当我尝试取消订阅时,我不再有对匿名函数的引用,因此无法将其从数组中删除。我知道您可以将匿名函数存储在变量中,但这不可行,因为我动态生成这些实体并且可能存在“无限”实体。即未知大小。

所以我一定在这里做错了什么。要么我做错了,那里有一个更简单的解决方案,要么我只是错过了一些可以解决我的问题的东西。Welcome 解决方案可以是完全不同的解决方案,也可以是成功订阅、取消订阅和发出的一种方式。谢谢!如果我能提供额外的说明,请告诉我。

class EventEmitter {
  constructor() {
    this.subscriptions = [];
  }

  subscribe(fn) {
    this.subscriptions.push(fn);
  }

  unsubscribe(fn) {
    this.subscriptions = this.subscriptions.filter(subscription => subscription !== fn);
  }

  emit(args) {
    // upon subscribing, you should get an error when doing trigger that amounts to
    // "message": "Uncaught TypeError: Cannot read property 'subscriptions' of undefined"
    // this happens because the line entities[0].eventEmitter.subscribe(entities[1].eventEmitter.emit);
    // with argument passed in entities[1].eventEmitter.emit refers to the window?
    // so this.subscriptions is not defined
    // console.log(this);
    console.log('emitting');
    this.subscriptions.forEach(subscription => subscription(args));
  }
}

class Entity {
  constructor()  {
    this.eventEmitter = new EventEmitter();
  }
}

const entities = [
  new Entity(),
  new Entity(),
];

document.getElementById('add').addEventListener('click', () => {
  // works, but requires an anonymous function that can't be removed
  entities[0].eventEmitter.subscribe(() => entities[1].eventEmitter.emit());
  
  // doesn't work because 'this' in the emit funciton refers to the window
  entities[0].eventEmitter.subscribe(entities[1].eventEmitter.emit);
}, false);

document.getElementById('trigger').addEventListener('click', () => {
  entities[0].eventEmitter.emit();
}, false);

document.getElementById('remove').addEventListener('click', () => {
  // how? ...
  // entities[0].eventEmitter.unsubscribe(entities[1].eventEmitter.emit);
}, false);
<button id='add'>add event listener</button>
<button id='trigger'>trigger emit on element 0</button>
<button id='remove'>remove event listener</button>

标签: javascriptclasseventsaddeventlistenerhigher-order-functions

解决方案


嗯,您可以做的是使用.bind()并将其绑定到您的事件发射器类

我创建了一个存储方法的数组。

class EventEmitter {
  constructor() {
    this.subscriptions = [];
  }

  subscribe(fn) {
    this.subscriptions.push(fn);
  }

  unsubscribe(fn) {
    this.subscriptions = this.subscriptions.filter(subscription => subscription !== fn);
  }

  emit(args) {
    // upon subscribing, you should get an error when doing trigger that amounts to
    // "message": "Uncaught TypeError: Cannot read property 'subscriptions' of undefined"
    // this happens because the line entities[0].eventEmitter.subscribe(entities[1].eventEmitter.emit);
    // with argument passed in entities[1].eventEmitter.emit refers to the window?
    // so this.subscriptions is not defined
    // console.log(this);
    this.subscriptions.forEach(subscription => {
       console.log("emit");
       subscription(args)
    });
  }
}

class Entity {
  constructor()  {
    this.eventEmitter = new EventEmitter();
  }
}

const entities = [
  new Entity(),
  new Entity(),
];

let methods = [];

document.getElementById('add').addEventListener('click', () => {
 
 
  let method = entities[1].eventEmitter.emit.bind(entities[1].eventEmitter)
  methods.push(method);
  entities[0].eventEmitter.subscribe(method);
}, false);

document.getElementById('trigger').addEventListener('click', () => {
  entities[0].eventEmitter.emit();
}, false);

document.getElementById('remove').addEventListener('click', () => {
   entities[0].eventEmitter.unsubscribe(methods.pop())
}, false);
<button id='add'>add event listener</button>
<button id='trigger'>trigger emit on element 0</button>
<button id='remove'>remove event listener</button>


推荐阅读