To infinity and beyond with JavaScript Proxy API

The Proxy API was introduced in 2015 with ES6 and has the potential to revolutionize the JavaScript development experience by making previously impossible feats possible. Although an advanced concept, mastering the Proxy API is crucial for anyone who wants to become proficient in JavaScript.

The Proxy API was introduced in ES6 with the goal of extending the meta-programming capabilities of JavaScript. Meta-programming is a technique that allows developers to alter the behavior of their programs, analyze their internal workings, and even modify the source code dynamically. With the Proxy API, developers can override internal JavaScript operations such as object property lookup, enumeration, assignment, iteration, function invocation, generator behavior, and much more. This gives developers the power to create more flexible and customizable code, making the Proxy API a valuable tool for advanced JavaScript development.

The basic terminology of the Proxy API includes these terms:

  • traps – the methods you are overriding. Here is the full list of possible traps, i.e. the list of functions that you can override using the Proxy API:
    • Object methods:
    • getPrototypeOf()
    • setPrototypeOf()
    • isExtensible()
    • preventExtensions()
    • getOwnPropertyDescriptor()
    • ownKeys()
    • Property getters/setters:
    • has()
    • get()
    • set()
    • deleteProperty()
    • Function methods:
    • apply()
    • construct()
  • handler – an object, whose properties are traps. This means you can define multiple traps on one handler!
  • target – object, whose methods are overridden

Using the GET trap

Now, let’s look at some examples. The easiest one I could think of is using the get trap to provide default values for undefined properties:

const defaultValueHandler = {
get: (obj, property) =>
property in obj ? obj[property] : 'general kenobi'
}
const objectWithDefaultValue = new Proxy({}, defaultValueHandler);
objectWithDefaultValue.a = 'b';
console.log(objectWithDefaultValue.a); // b
console.log(objectWithDefaultValue['hello there']); // general kenobi

You can see that on lines 1-4 we create a handler for the proxy. It has a get function defined, which overrides the property lookup behavior. In case a property is not defined on the object, we return 'general kenobi‘ instead of undefined. Then, on line 6 we use the Proxy API to make that happen. Note the new keyword, it is required as Proxy is a constructor function. Lastly, on lines 10-11 you can see our proxy in action: for the non-existent 'hello there' property, the default value of 'general kenobi' is returned.

Using the SET trap

Now let’s move to a more advanced example. We will use the set trap to (1) validate the property value to make sure its a string, and (2) convert the string to uppercase. Here is the code:

const stringValidatorHandler = {
set: (obj, key, value) => {
if (typeof value !== 'string') {
throw new Error('Expected string!')
}
obj[key] = value.toUpperCase();
}
}
const weirdObject = new Proxy({}, stringValidatorHandler);
weirdObject.a = 5 // Error: Expected string!
weirdObject.a = 'abacaba';
console.log(weirdObject.a); // ABACABA

Now we use the set trap in the handler to override the property assignment behavior. It first checks if the value is a string, and, if it is, saves an upper case version on the object with the specified key. You can see how it behaves on lines 13-15: throws if you try to pass 5 and converts 'abacaba' to 'ABACABA'.

I hope now you have an understanding of the Proxy API in JavaScript and how to use it. Let me know about your experience with this technique and what problems did you solve with it!

Read more: 

  1. Introducing JavaScript Proxy
  2. Unleashing the Power of JavaScript Proxy: A Comprehensive Introduction for Developers

No comments:

Post a Comment