JavaScript Proxies and Reflect
Intercept and customize object operations with Proxy traps and Reflect
Overview
A Proxy wraps a target object and lets you intercept fundamental operations—reading, writing, deleting, and more—through handler functions called traps. The Reflect object provides methods that mirror those operations, giving traps a clean way to forward to default behavior. Together they power reactive frameworks, validation layers, and observable data.
Syntax / Usage
Create a proxy with new Proxy(target, handler). Each trap receives the target plus operation-specific arguments; Reflect supplies the matching default implementation.
const target = { count: 0 }
const handler = {
get(obj, prop, receiver) {
return Reflect.get(obj, prop, receiver)
},
set(obj, prop, value, receiver) {
if (prop === 'count' && typeof value !== 'number') {
throw new TypeError('count must be a number')
}
return Reflect.set(obj, prop, value, receiver)
},
}
const proxy = new Proxy(target, handler)
proxy.count = 5 // ok
console.log(proxy.count) // 5
Examples
Provide default values for missing properties:
function withDefault(obj, fallback) {
return new Proxy(obj, {
get(target, prop, receiver) {
return prop in target ? Reflect.get(target, prop, receiver) : fallback
},
})
}
const settings = withDefault({ theme: 'dark' }, 'unset')
console.log(settings.theme) // 'dark'
console.log(settings.locale) // 'unset'
Log every property access for debugging:
function trace(obj, name) {
return new Proxy(obj, {
get(target, prop, receiver) {
console.log(`${name}.${String(prop)}`)
return Reflect.get(target, prop, receiver)
},
})
}
const user = trace({ id: 1, name: 'Ada' }, 'user')
user.name // logs "user.name", returns 'Ada'
Validate function arguments with the apply trap:
function positiveOnly(fn) {
return new Proxy(fn, {
apply(target, thisArg, args) {
if (args.some((n) => n < 0)) throw new RangeError('no negatives')
return Reflect.apply(target, thisArg, args)
},
})
}
const safeSum = positiveOnly((a, b) => a + b)
console.log(safeSum(2, 3)) // 5
Common Mistakes
- Forgetting to return
truefrom thesettrap in strict mode, which throws - Not forwarding to
Reflect, so traps break default behavior for prototypes and receivers - Assuming
===on a proxy equals its target—they are distinct references - Adding heavy logic in
get, slowing down every property read - Trying to proxy primitives; the target must be an object or function
See Also
objects functions javascript-prototypes