Skip to main content
Piotr Majkutewicz
Back to Blog

JavaScript Proxy Pattern: A Practical Guide

The Proxy object in JavaScript enables you to create a wrapper around another object, intercepting and redefining fundamental operations like property lookup, assignment, and enumeration.

Basic Syntax

javascript
const target = {
  name: 'John',
  age: 30
};

const handler = {
  get: function(target, prop) {
    return prop in target ? target[prop] : 'Property not found';
  }
};

const proxy = new Proxy(target, handler);
console.log(proxy.name); // 'John'
console.log(proxy.unknown); // 'Property not found'

Common Use Cases

1. Validation

Ensure properties meet certain criteria before they're set.

javascript
const userValidator = {
  set: function(obj, prop, value) {
    if (prop === 'age') {
      if (!Number.isInteger(value)) {
        throw new TypeError('Age must be an integer');
      }
      if (value < 0 || value > 120) {
        throw new RangeError('Age must be between 0 and 120');
      }
    }
    obj[prop] = value;
    return true;
  }
};

const user = new Proxy({}, userValidator);
user.age = 30; // Works
user.age = -1; // Throws RangeError
user.age = 'young'; // Throws TypeError

2. Logging

Track access and modifications to objects.

javascript
const loggingHandler = {
  get: function(target, prop) {
    console.log(`Accessing property: ${prop}`);
    return target[prop];
  },
  set: function(target, prop, value) {
    console.log(`Setting property: ${prop} = ${value}`);
    target[prop] = value;
    return true;
  }
};

const logged = new Proxy({}, loggingHandler);

3. Default Values

Provide fallback values for undefined properties.

javascript
const withDefaults = new Proxy({}, {
  get: function(target, prop) {
    return target[prop] ?? 'default value';
  }
});

console.log(withDefaults.nonexistent); // 'default value'

Advanced Example: Private Properties

javascript
const privateProxy = (target) => {
  return new Proxy(target, {
    get: function(target, prop) {
      if (prop.startsWith('_')) {
        throw new Error('Access to private property denied');
      }
      return target[prop];
    }
  });
};

const obj = privateProxy({ _private: 'secret', public: 'open' });
console.log(obj.public); // 'open'
console.log(obj._private); // Error: Access to private property denied

Best Practices

  • Use Proxies for cross-cutting concerns like validation and logging
  • Keep handler functions pure and predictable
  • Document the behavior of your Proxy handlers
  • Consider using Reflect methods in your handlers
  • Be mindful of performance in performance-critical code