Create a Proxy wrapper

phuocng

Phuoc Nguyen

Posted on January 18, 2024

Create a Proxy wrapper

In our previous post, we explored the basics of creating and using a Proxy object. We customized the set trap to add a simple validation to our target. Here's the code snippet that demonstrates what we did earlier:

const handler = {
    set: function(target, prop, value) {
        if (prop === 'age' && value < 0 || value > 120) {
            throw new Error('Invalid age');
            return;
        }
        target[prop] = value;
    },
};
Enter fullscreen mode Exit fullscreen mode

In the set trap handler, we check if the user is trying to set the age property by looking at the prop parameter. Then, we verify if the value parameter is less than 0 or greater than 120. If both conditions are true, we throw an exception.

After that, we can create a Proxy for our person object using the Proxy constructor.

const target = {
    name: 'John Smith',
    age: 30,
};
const proxy = new Proxy(target, handler);
Enter fullscreen mode Exit fullscreen mode

Limitations to address

While this implementation is effective and gives us the power of Proxy, there are a few limitations we need to address.

Firstly, exposing the Proxy to end users is typically not a good idea. We should design the implementation in a way that hides the details.

Encapsulation is a fundamental concept in programming. It involves organizing code so that data and methods are grouped together as a single unit, such as a class. This principle also involves information hiding, which means that implementation details should be hidden from users. This is often referred to as the principle of least knowledge or the law of demeter (LoD). By following these principles, we can create more maintainable and robust code that is less prone to bugs.

In our case, it's important not to expose the inner workings of our Proxy implementation to users unless it's absolutely necessary.

The example we've been working with only applies to a specific target, like a person object. This makes it tough to reuse our code in different projects or with different objects. We'd have to copy and paste our code every time we want to use it on a new object.

Plus, if we wanted to add more traps or change how our proxy behaves, we'd have to update each instance of our code that uses that particular set of traps. This can get messy and lead to errors.

A better approach

To solve these issues, we can create a wrapper function around our Proxy object that only shows the necessary interface while keeping the implementation details concealed. This is a great way to use the Proxy object effectively. By creating a wrapper, we can encapsulate the behavior we want to apply to our objects in a single function. This makes it much easier to use and manage.

For instance, let's say we have multiple person objects with different properties that require different validation rules. Instead of defining unique handlers for each object, we can create a single withValidators wrapper function. This function takes an object as an argument and returns a new proxy object with customized behavior that meets our validation requirements.

Here's a simple implementation of a wrapper that validates the age property.

const withValidators = (person) => {
    const handler = {
        set: function(target, prop, value) {
            if (prop === 'age' && value < 0 || value > 120) {
                throw new Error('Invalid age');
                return;
            }
            target[prop] = value;
        },
    };

    return new Proxy(person, handler);
};
Enter fullscreen mode Exit fullscreen mode

To use the withValidators wrapper function, we just need to pass an object as an argument. The function will then return a new proxy object that applies customized behavior to the properties of the original object.

Here's an example of how to call the withValidators function:

const person = withValidators({
    name: 'John Smith',
    age: 30,
});
Enter fullscreen mode Exit fullscreen mode

In this example, we passed an object with name and age properties to the withValidators function. The function returned a new proxy object that applies custom validation rules for the age property.

Once we have our person object wrapped in a proxy, we can access or set its properties just like any other object. However, if we try to set an invalid value to the age property, the custom validator in the handler will throw an error. This ensures that our person object meets the validation requirements we've set.

console.log(person.age);     // 30

person.age = 42;
person.age = -5;             // Throws an exception
Enter fullscreen mode Exit fullscreen mode

As you can see, using a wrapper makes it easier to reuse our code and ensures consistency across all our objects. Plus, if we need to change how our proxies behave, we only have to update the wrapper function instead of changing each handler one by one. This saves us time and effort in the long run.

Conclusion

To sum up, we've learned how to create a Proxy wrapper function that enables us to encapsulate the behavior we want to apply to our objects in one place. Wrapping our objects in a proxy with custom validation rules can help ensure consistency and reusability of our code.

It also simplifies the process of modifying the behavior of all our proxies at once by updating the wrapper function instead of modifying every handler individually.


If you found this series helpful, please consider giving the repository a star on GitHub or sharing the post on your favorite social networks 😍. Your support would mean a lot to me!

If you want more helpful content like this, feel free to follow me:

💖 💪 🙅 🚩
phuocng
Phuoc Nguyen

Posted on January 18, 2024

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related

Create a Proxy wrapper
tutorial Create a Proxy wrapper

January 18, 2024