Proxy Basic Usage

ES6 Proxy 的基本用法和常见拦截操作

问题

如何使用 ES6 的 Proxy 创建对象代理,实现对对象操作的拦截?

解答

基本语法

const proxy = new Proxy(target, handler)
// target: 要代理的目标对象
// handler: 定义拦截行为的对象

常用拦截操作

const target = {
  name: 'Alice',
  age: 25
}

const handler = {
  // 拦截属性读取
  get(target, prop, receiver) {
    console.log(`读取属性: ${prop}`)
    return Reflect.get(target, prop, receiver)
  },

  // 拦截属性设置
  set(target, prop, value, receiver) {
    console.log(`设置属性: ${prop} = ${value}`)
    return Reflect.set(target, prop, value, receiver)
  },

  // 拦截 in 操作符
  has(target, prop) {
    console.log(`检查属性: ${prop}`)
    return Reflect.has(target, prop)
  },

  // 拦截 delete 操作
  deleteProperty(target, prop) {
    console.log(`删除属性: ${prop}`)
    return Reflect.deleteProperty(target, prop)
  }
}

const proxy = new Proxy(target, handler)

proxy.name          // 读取属性: name -> 'Alice'
proxy.age = 30      // 设置属性: age = 30
'name' in proxy     // 检查属性: name -> true
delete proxy.age    // 删除属性: age

实际应用:数据验证

function createValidator(target, rules) {
  return new Proxy(target, {
    set(target, prop, value) {
      const rule = rules[prop]
      if (rule && !rule.validate(value)) {
        throw new Error(rule.message)
      }
      return Reflect.set(target, prop, value)
    }
  })
}

const user = createValidator({}, {
  age: {
    validate: v => typeof v === 'number' && v >= 0 && v <= 150,
    message: 'age 必须是 0-150 的数字'
  },
  email: {
    validate: v => /^[\w-]+@[\w-]+\.\w+$/.test(v),
    message: 'email 格式不正确'
  }
})

user.age = 25           // 正常
user.email = 'a@b.com'  // 正常
user.age = -1           // Error: age 必须是 0-150 的数字

实际应用:响应式数据

function reactive(obj, callback) {
  return new Proxy(obj, {
    set(target, prop, value) {
      const oldValue = target[prop]
      const result = Reflect.set(target, prop, value)
      if (oldValue !== value) {
        callback(prop, value, oldValue)
      }
      return result
    }
  })
}

const state = reactive({ count: 0 }, (prop, newVal, oldVal) => {
  console.log(`${prop}: ${oldVal} -> ${newVal}`)
})

state.count++  // count: 0 -> 1
state.count++  // count: 1 -> 2

常用 handler 方法

方法拦截操作
get读取属性
set设置属性
hasin 操作符
deletePropertydelete 操作
apply函数调用
constructnew 操作
getPrototypeOfObject.getPrototypeOf
ownKeysObject.keys / for…in

关键点

  • Proxy 可以拦截对象的 13 种基本操作
  • 配合 Reflect 使用,保持默认行为的同时添加自定义逻辑
  • Vue 3 的响应式系统基于 Proxy 实现
  • Proxy 是惰性的,只有访问时才触发拦截
  • 与 Object.defineProperty 相比,Proxy 可以拦截数组变化和新增属性