You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

除事件监听模式外,JavaScript不同对象间通信是否有更优实现方案?

Great job getting started with event-driven communication between objects—your current implementation nails the core idea of connecting emitters and listeners, but it has a critical limitation: each event can only have one listener attached at a time (since you’re overwriting the event1/event2 properties directly). Let’s walk through some more robust, flexible alternatives that fix this and scale better for real-world use:

1. Improved Observer Pattern (Supports Multiple Listeners)

This is a direct upgrade to your existing code that lets multiple listeners subscribe to the same event—just like standard DOM event handling. Instead of storing a single function per event, we use arrays to hold all active listeners.

class Emitter {
  constructor() {
    // Store listeners in an object of arrays, one per event
    this.events = {
      event1: [],
      event2: []
    };
  }

  someEmitterLogic(input) {
    // Trigger ALL listeners for event1
    this.events.event1.forEach(listener => listener({message:"produced at event1", b:1}));
    // Trigger ALL listeners for event2
    this.events.event2.forEach(listener => listener({message:"produced at event2", d:2}));
  }

  addEvent(event, listener) {
    if (this.events.hasOwnProperty(event)) {
      // Optional: Prevent duplicate listener registrations
      if (!this.events[event].includes(listener)) {
        this.events[event].push(listener);
      }
    } else {
      console.log("Sorry, we don't emit this event.");
    }
  }

  removeEvent(event, listener) {
    if (this.events.hasOwnProperty(event)) {
      // Filter out the specific listener to remove
      this.events[event] = this.events[event].filter(fn => fn !== listener);
    } else {
      console.log("Sorry, cannot remove an event we are not emitting.");
    }
  }

  // Optional: Clear all listeners for an event
  removeAllListeners(event) {
    if (this.events.hasOwnProperty(event)) {
      this.events[event] = [];
    }
  }
}

// Usage stays mostly the same, but now you can add multiple listeners!
const emitter = new Emitter();
const listener = new Listener();
emitter.addEvent("event1", listener.listener1);
emitter.addEvent("event1", (e) => console.log(`Second listener got: ${e.message}`)); // Works now!

Why this works better:

  • Supports multiple listeners per event without overwriting existing ones
  • Lets you remove specific listeners (not just reset the entire event)
  • Aligns with familiar event system behavior (like addEventListener in the browser)

2. Publish/Subscribe (Pub/Sub) Pattern

If you want full decoupling between emitters and listeners (they don’t need to know about each other at all), use a Pub/Sub "message broker" as a middleman. Emitters publish events to the broker, and listeners subscribe to events from the broker.

// Central Pub/Sub broker
class PubSub {
  constructor() {
    this.topics = {};
  }

  // Subscribe to a topic, returns a function to unsubscribe later
  subscribe(topic, listener) {
    if (!this.topics[topic]) {
      this.topics[topic] = [];
    }
    this.topics[topic].push(listener);
    return () => {
      this.topics[topic] = this.topics[topic].filter(fn => fn !== listener);
    };
  }

  // Publish data to a topic
  publish(topic, data) {
    if (this.topics[topic]) {
      this.topics[topic].forEach(listener => listener(data));
    }
  }
}

// Emitter now only interacts with the PubSub broker
class Emitter {
  constructor(pubSub) {
    this.pubSub = pubSub;
  }

  someEmitterLogic(input) {
    this.pubSub.publish("event1", {message:"produced at event1", b:1});
    this.pubSub.publish("event2", {message:"produced at event2", d:2});
  }
}

// Listener also only interacts with the PubSub broker
class Listener{
  constructor(pubSub){
    // Store unsubscribe functions for easy cleanup
    this.unsubscribeEvent1 = pubSub.subscribe("event1", (e) => console.log(e.message));
    this.unsubscribeEvent2 = pubSub.subscribe("event2", (e) => console.log(e.message));
  }

  stopListeningToEvent1() {
    this.unsubscribeEvent1();
  }
}

// Usage
const pubSub = new PubSub();
const emitter = new Emitter(pubSub);
const listener = new Listener(pubSub);

emitter.someEmitterLogic(); // Both listeners fire
listener.stopListeningToEvent1();
emitter.someEmitterLogic(); // Only event2 listener fires

Why this works better:

  • Complete decoupling: Emitters and listeners don’t reference each other directly
  • Scales easily to multiple emitters and listeners
  • Clean unsubscribe flow with returned functions

3. ES6 Proxy (For Property-Based Notifications)

If your use case involves reacting to changes in an object’s properties (instead of explicit event triggers), an ES6 Proxy can automatically notify listeners when properties are updated. This is great for state-tracking scenarios.

class Emitter {
  constructor() {
    this.data = {
      event1Data: null,
      event2Data: null
    };
    this.event1Listeners = [];
    this.event2Listeners = [];

    // Create a Proxy to intercept property changes
    this.proxiedData = new Proxy(this.data, {
      set: (target, prop, value) => {
        target[prop] = value;
        // Trigger listeners when specific properties change
        if (prop === "event1Data") {
          this.event1Listeners.forEach(listener => listener(value));
        } else if (prop === "event2Data") {
          this.event2Listeners.forEach(listener => listener(value));
        }
        return true;
      }
    });
  }

  someEmitterLogic(input) {
    // Updating the proxied property triggers notifications
    this.proxiedData.event1Data = {message:"produced at event1", b:1};
    this.proxiedData.event2Data = {message:"produced at event2", d:2};
  }

  addEvent(event, listener) {
    if (event === "event1") {
      this.event1Listeners.push(listener);
    } else if (event === "event2") {
      this.event2Listeners.push(listener);
    } else {
      console.log("Sorry, we don't emit this event.");
    }
  }
}

Why this works better:

  • Automates notifications when properties change (no manual event triggers)
  • Clean for state-driven applications

Which to Choose?

  • Use the improved observer pattern if you want to upgrade your existing code with minimal changes and support multiple listeners.
  • Use Pub/Sub if you need full decoupling between emitters and listeners (great for large apps with many components).
  • Use Proxy if your communication is tied to object property changes instead of explicit events.

内容的提问来源于stack exchange,提问作者user 4234-483244-34

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.28 13:42:28