What is a Proxy?

A Proxy object is a wrapper around another object (the target) that allows you to define custom behavior for fundamental operations. The Proxy constructor takes two arguments: the target object and a handler object that defines the traps (intercepting methods) for the operations you want to customize.

Basic Syntax

const proxy = new Proxy(target, handler);
  • target: The original object that you want to wrap.
  • handler: An object that defines the traps for the proxy.

Example of a Simple Proxy

Let's create a simple proxy that logs every time a property is accessed on an object.

const target = {
    message: "Hello, World!"
};

const handler = {
    get: function(target, prop, receiver) {
        console.log(`Property ${prop} was accessed.`);
        return Reflect.get(target, prop, receiver);
    }
};

const proxy = new Proxy(target, handler);

console.log(proxy.message); // Logs: Property message was accessed. \n Hello, World!

Common Use Cases for Proxies

1. Validation

You can use proxies to enforce validation rules on object properties. For instance, let's create a proxy that only allows positive numbers to be set for a property.

const target = {};

const handler = {
    set: function(target, prop, value) {
        if (typeof value !== 'number' || value < 0) {
            throw new Error(`Invalid value for ${prop}: ${value}. Must be a positive number.`);
        }
        target[prop] = value;
        return true;
    }
};

const proxy = new Proxy(target, handler);

try {
    proxy.age = 25; // Valid
    console.log(proxy.age); // 25
    proxy.age = -5; // Throws error
} catch (error) {
    console.error(error.message); // Invalid value for age: -5. Must be a positive number.
}

2. Property Access Logging

You can enhance your debugging process by logging property accesses and modifications. Here's how you can implement a logging proxy.

const target = {
    name: "Alice",
    age: 30
};

const handler = {
    get: function(target, prop) {
        console.log(`Getting property ${prop}`);
        return Reflect.get(target, prop);
    },
    set: function(target, prop, value) {
        console.log(`Setting property ${prop} to ${value}`);
        return Reflect.set(target, prop, value);
    }
};

const proxy = new Proxy(target, handler);

console.log(proxy.name); // Logs: Getting property name \n Alice
proxy.age = 31; // Logs: Setting property age to 31

3. Creating a Reactive Object

Proxies can be used to create reactive data structures, similar to frameworks like Vue.js. Here’s a simple implementation.

function reactive(target) {
    const handler = {
        set(target, prop, value) {
            console.log(`Property ${prop} set to ${value}`);
            target[prop] = value;
            return true;
        }
    };
    return new Proxy(target, handler);
}

const state = reactive({ count: 0 });

state.count = 1; // Logs: Property count set to 1
state.count = 2; // Logs: Property count set to 2

Comparison of Proxy vs. Object.defineProperty

While both Proxy and Object.defineProperty can be used to define custom behavior for property access, there are key differences between them:

FeatureProxyObject.defineProperty
Intercept all operationsYesNo (only for defined properties)
Can intercept arraysYesNo
Can create dynamic trapsYesNo
Performance overheadHigher due to interceptionLower, as it modifies properties directly
Simplicity of useMore complexSimpler for basic use cases

Best Practices

  1. Use Proxies Sparingly: Proxies can introduce complexity and performance overhead, so use them only when necessary.
  2. Avoid Side Effects: Ensure that the traps do not introduce unexpected side effects that could lead to difficult-to-debug issues.
  3. Combine with Other Patterns: Use proxies in conjunction with other design patterns (like the Observer pattern) to create more robust applications.

Conclusion

The Proxy object in JavaScript is a versatile tool that can enhance your applications by allowing you to customize fundamental operations on objects. By understanding its capabilities and best practices, you can leverage proxies to create more maintainable and flexible code.

Learn more with useful resources: