JS Meta Programming & Proxy API
Unlock internal JavaScript engine mechanics: intercept fundamental object operations with Proxy, forward invocations with Reflect, build reactive UI state engines, and implement capability security.
Proxy & Reflect Interception Architecture
A Proxy wraps a target object and traps internal calls (like property access, assignment, iteration, and deletion), allowing you to inject validation, logging, or reactive DOM updates.
1. The Proxy Constructor & Essential Traps
A Proxy wraps an existing JavaScript object and overrides its fundamental semantics. The second parameter is a handler object containing interceptor functions known as traps.
| Trap Signature | Intercepted Syntax | Common Real-World Purpose |
|---|---|---|
get(target, prop, receiver) |
obj.prop, obj['prop'] |
Default values, negative array indices, lazy property getters. |
set(target, prop, value, receiver) |
obj.prop = value |
Type enforcement, range validation, reactive UI state re-rendering. |
has(target, prop) |
'prop' in obj |
Hiding private properties (e.g. prefix _) from property inspection. |
deleteProperty(target, prop) |
delete obj.prop |
Preventing deletion of critical system or immutable configuration keys. |
apply(target, thisArg, args) |
fn(...args), fn.call() |
Function execution profiling, performance timing, memoization. |
Python-Style Negative Array Indexing
function createSmartArray(...items) {
return new Proxy(items, {
get(target, prop, receiver) {
const index = Number(prop);
if (Number.isInteger(index) && index < 0) {
// Map -1 to last item, -2 to second-to-last, etc.
return target[target.length + index];
}
return Reflect.get(target, prop, receiver);
}
});
}
const arr = createSmartArray('Ruby', 'JavaScript', 'Python', 'Go');
console.log(arr[-1]); // 'Go' (Last item!)
console.log(arr[-2]); // 'Python'
Try in Playground
2. Why Reflect is Indispensable: The receiver Trap
Never manually forward traps using raw object syntax (like target[prop] = value). Always delegate to the matching Reflect method! Passing the receiver argument guarantees that prototypal inheritance and getter methods preserve the proper this context.
const handler = {
get(target, prop) {
// ❌ WRONG: If target has a getter that references 'this',
// 'this' binds to target, NOT the proxy!
return target[prop];
}
};
const handler = {
get(target, prop, receiver) {
// ✅ CORRECT: receiver ensures getters evaluate
// with 'this' bound correctly to the proxy!
return Reflect.get(target, prop, receiver);
}
};
Interactive Lab: Mini Reactive State Engine
Vue 3 Reactivity Model
Below is a live reactive store implemented with Proxy. Mutating the state properties immediately re-renders the DOM view without manual DOM manipulation.
Build a Runtime Schema Validator Proxy
Implement a createValidatedObject(schema, initialValues) function. When any property is set on the proxy, check that it matches the type declared in the schema (e.g. { age: 'number', email: 'string' }). If the assigned type is wrong, throw a descriptive TypeError.
function createValidatedObject(schema, target = {}) {
return new Proxy(target, {
set(obj, prop, value, receiver) {
if (prop in schema) {
const expectedType = schema[prop];
const actualType = typeof value;
if (actualType !== expectedType) {
throw new TypeError(`Invalid type for '${prop}': Expected ${expectedType}, received ${actualType}`);
}
}
return Reflect.set(obj, prop, value, receiver);
}
});
}
// Test validation:
const schema = { name: 'string', age: 'number' };
const user = createValidatedObject(schema);
user.name = 'Alex'; // ✅ Valid
user.age = 29; // ✅ Valid
// user.age = 'twenty'; // ❌ Throws TypeError!
Chapter 34 Knowledge Check
Validate your comprehension of Proxy traps, Reflect methods, and the receiver parameter.
set trap in a Proxy handler return to indicate a successful assignment?in operator is used (e.g. 'id' in proxy)?receiver parameter to Reflect.get(target, prop, receiver)?Proxy.revocable() used for?