Chalarangelo/30-seconds-of-code

View on GitHub
content/snippets/js/s/observable-proxy.md

Summary

Maintainability
Test Coverage
---
title: Using a Proxy to implement the Observable pattern
shortTitle: Observable Proxy
type: story
language: javascript
tags: [object,proxy]
cover: city-view
excerpt: Use the Proxy object to implement the Observable pattern in JavaScript.
listed: true
dateModified: 2024-05-27
---

The **Observer pattern** is a design pattern where an object (known as the subject) maintains a list of **dependents** (observers) that are notified of any changes in the object's state. With a little ingenuity, we can leverage the `EventTarget` interface and the `Proxy` object to implement the Observer pattern in JavaScript.

> [!NOTE]
>
> I'm using the `EventTarget` interface, as it's **common between the browser and Node.js** environments. If you're only working with Node.js, the `EventEmitter` offers a more robust solution, but you'll have to make a few tweaks.

At its heart the Observer pattern is a simple pub/sub ([publish–subscribe](https://en.wikipedia.org/wiki/Publish%E2%80%93subscribe_pattern)) system. We can create a `Observable` class that extends the `EventTarget` interface and use a `Proxy` object to **intercept property changes**, via the `set` trap.

When a property changes, a `CustomEvent` is created to notify any observers, carrying the **name of the property** as its `type` and the **new value** as its `detail`. Finally, the event will be **dispatched** via `EventTarget.dispatchEvent()`, **notifying all registered listeners**.

```js
class Observable extends EventTarget {
  constructor() {
    super();
    return new Proxy(this, {
      set: (target, property, value) => {
        target[property] = value;
        this.dispatchEvent(new CustomEvent(property, { detail: value }));
        return true;
      },
    });
  }
}

const subject = new Observable();

subject.addEventListener('name', event => {
  console.log(`Name changed to ${event.detail}`);
});

subject.name = 'Alice'; // Name changed to Alice
```

While this code is pretty simple, it can be easily extend to handle more complex use cases, such as **nested objects** or **arrays**. It can be even used to create an observable store, similar to Redux. Give it a try and see how far you can take it!