Proxy
What it is
Section titled “What it is”Proxy creates a wrapper around an object, allowing you to intercept and customize fundamental operations (get, set, delete, etc.). Introduced in ES6 (2015).
Before this feature
Section titled “Before this feature”Object operations couldn’t be customized:
// No way to intercept property accessconst 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; }};After this feature
Section titled “After this feature”Proxies enable powerful meta-programming:
// Basic proxyconst 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 nameproxy.age = 25; // logs: Setting age = 25
// Validationconst 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; // OKperson.age = "old"; // TypeError
// Default valuesconst 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]); // 5console.log(arr[-2]); // 4
// Private propertiesconst 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 trappingconst 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,3Why this is better
Section titled “Why this is better”- 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
Key notes / edge cases
Section titled “Key notes / edge cases”- 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)
ReflectAPI provides default behaviors- Performance overhead from interception
// All trap typesconst 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 defaultsconst proxy = new Proxy(target, { get(target, prop, receiver) { console.log(`Get: ${prop}`); return Reflect.get(target, prop, receiver); }});Quick practice
Section titled “Quick practice”-
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;}}); -
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];}}); -
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.