allBlogsList

An Introduction to ES6 Proxies

Meta Programming with Proxies and the Reflection API

Proxies are a powerful new feature that was added to the JavaScript language in the ECMAScript 2015 specification. They expose the inner workings of objects and allow developers to intercept and alter low-level operations that were previously hidden away from us. In this post I'll go over how proxies work, along with why they are useful.

Example 1 - Getting an Object Property

A proxy can be used to control interactions with an object. It allows you to create "traps" to intercept and alter low-level operations. For example, here's how you can create a proxy to intercept and alter what happens when a specific property is being read:

const person = {
    name: 'John',
    age: 42,
};

const proxy = new Proxy(person, {
    get(obj, prop) {
        if (prop === 'name') {
            return 'Bob';
        }

        return Reflect.get(obj, prop);
    }
});

console.log(person.name); // "John"
console.log(person.age);  // 42

console.log(proxy.name);  // "Bob"
console.log(proxy.age);   // 42

proxy.age = 21;

console.log(person.age);  // 21
console.log(proxy.age);   // 21

In the example above we used a get trap to alter the return value of the "name" property. Notice how other operations behave normally, such as the update to the "age" property - which is reflected in both the original object as well as the proxy.

Also notice how we call Reflect.get when any other object key besides "name" is accessed. Reflect.get is part of the "Reflection API" - and its usage here essentially means "fall back to the default behavior".

 

Example 2 - Setting an Object Property

 As you have probably already assumed, you can intercept write operations as well. In this next example we're going to use a set trap to override the behavior when someone updates the "name" property in our person object. Whenever they do, we're going to automatically append a few exclamation points:

const person = {};

const proxy = new Proxy(person, {
    set(obj, prop, val) {
        if (prop === 'name') {
            return Reflect.set(obj, prop, `${ val } !!!`);
        }

        return Reflect.set(obj, prop, val);
    }
});

proxy.name = 'John';

console.log(person.name); // "John !!!"
console.log(proxy.name);  // "John !!!"

Additional Traps

There are many more traps besides get and set that can be used. The table below illustrates which traps are available, as well as which behavior they are overriding. I encourage you to try each of these out for yourself:

proxy-traps

<sub><em>* table illustration taken from "Understanding ES6" by Nicholas C. Zakas</em></sub>

TL; DR;

ES6 proxies allow us to tap into low-level object operations that were previously unavailable to JavaScript developers. This is achieved by setting "traps" to add new behavior.

In addition, the Reflection API allows us to fallback to the default behavior when necessary.