stackademic

The leading education platform for anyone with an interest in software development.

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 true from the set trap 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