Skip to content

Proxy

Proxy creates a wrapper around an object, allowing you to intercept and customize fundamental operations (get, set, delete, etc.). Introduced in ES6 (2015).

Object operations couldn’t be customized:

// No way to intercept property access
const obj = {};
obj.x = 1; // Can't hook into this
// Had to use getters/setters (limited)
const user = {
_age: 0,
get age() { return this._age; },
set age(value) {
if (value < 0) throw new Error('Invalid age');
this._age = value;
}
};

Proxies enable powerful meta-programming:

// Basic proxy
const target = { name: "Alice" };
const handler = {
get(target, prop) {
console.log(`Getting ${prop}`);
return target[prop];
},
set(target, prop, value) {
console.log(`Setting ${prop} = ${value}`);
target[prop] = value;
return true;
}
};
const proxy = new Proxy(target, handler);
proxy.name; // logs: Getting name
proxy.age = 25; // logs: Setting age = 25
// Validation
const validator = {
set(target, prop, value) {
if (prop === 'age' && typeof value !== 'number') {
throw new TypeError('Age must be a number');
}
target[prop] = value;
return true;
}
};
const person = new Proxy({}, validator);
person.age = 25; // OK
person.age = "old"; // TypeError
// Default values
const withDefaults = new Proxy({}, {
get(target, prop) {
return prop in target ? target[prop] : 'default';
}
});
withDefaults.name = "Bob";
console.log(withDefaults.name); // "Bob"
console.log(withDefaults.age); // "default"
// Negative array indices (Python-style)
const createArray = (arr) => new Proxy(arr, {
get(target, prop) {
const index = Number(prop);
if (index < 0) {
return target[target.length + index];
}
return target[prop];
}
});
const arr = createArray([1, 2, 3, 4, 5]);
console.log(arr[-1]); // 5
console.log(arr[-2]); // 4
// Private properties
const privateProps = new Proxy({}, {
get(target, prop) {
if (prop.startsWith('_')) {
throw new Error('Cannot access private property');
}
return target[prop];
},
set(target, prop, value) {
if (prop.startsWith('_')) {
throw new Error('Cannot set private property');
}
target[prop] = value;
return true;
}
});
// Function call trapping
const traceCalls = (fn) => new Proxy(fn, {
apply(target, thisArg, args) {
console.log(`Calling with args: ${args}`);
return target.apply(thisArg, args);
}
});
const sum = traceCalls((a, b) => a + b);
sum(2, 3); // logs: Calling with args: 2,3
  • Meta-programming: Customize object behavior
  • Validation: Intercept and validate operations
  • Logging: Track property access/changes
  • Flexible: Many trap types available
  • Transparent: Proxy appears like normal object
  • 13 trap types available (get, set, has, deleteProperty, etc.)
  • Can’t proxy primitives (only objects)
  • Traps must follow invariants (e.g., can’t report non-configurable property as non-existent)
  • Reflect API provides default behaviors
  • Performance overhead from interception
// All trap types
const handler = {
get(target, prop, receiver) {},
set(target, prop, value, receiver) {},
has(target, prop) {},
deleteProperty(target, prop) {},
ownKeys(target) {},
getOwnPropertyDescriptor(target, prop) {},
defineProperty(target, prop, descriptor) {},
getPrototypeOf(target) {},
setPrototypeOf(target, proto) {},
isExtensible(target) {},
preventExtensions(target) {},
apply(target, thisArg, args) {},
construct(target, args, newTarget) {}
};
// Using Reflect for defaults
const proxy = new Proxy(target, {
get(target, prop, receiver) {
console.log(`Get: ${prop}`);
return Reflect.get(target, prop, receiver);
}
});
  1. Create a proxy that converts all property values to uppercase

    Answer
    const uppercase = new Proxy({}, {
    get(target, prop) {
    const value = target[prop];
    return typeof value === 'string' ? value.toUpperCase() : value;
    }
    });
  2. Make an array accessible with negative indices

    Answer
    const arr = new Proxy([1,2,3], {
    get(target, prop) {
    const index = Number(prop);
    if (index < 0) return target[target.length + index];
    return target[prop];
    }
    });
  3. What’s the difference between Proxy and Object.defineProperty?

    Answer Proxy intercepts operations on the entire object; defineProperty defines specific property behavior. Proxy is more powerful and flexible.