JS的Proxy对象详解

61 阅读1分钟

是什么?

Proxy 对象,是ES6引入的一个强大特性,用于创建一个对象的代理,从而实现基本操作的拦截和自定义(如属性查找、赋值、枚举、函数调用等)。

1. 基本语法

const proxy = new Proxy(target, handler)
// target: 要包装的目标对象
// handler: 一个对象,包含各种"陷阱"(trap)方法

2. 常用陷阱方法

get 拦截
const obj = { name: '张三' }
const proxy = new Proxy(obj, {
  get(target, property) {
    console.log(`读取属性: ${property}`)
    return target[property] || '默认值'
  }
})

console.log(proxy.name) // 读取属性: name \n 张三
console.log(proxy.age)  // 读取属性: age \n 默认值
set 拦截
const validator = {
  set(target, key, value) {
    if (key === 'age') {
      if (!Number.isInteger(value) || value < 0) {
        throw new TypeError('年龄必须是正整数')
      }
    }
    target[key] = value
    return true // 表示设置成功
  }
}

const person = new Proxy({}, validator)
person.age = 25  // 正常
person.age = -5  // 报错: 年龄必须是正整数
apply 拦截函数调用
function sum(a, b) {
  return a + b
}

const proxy = new Proxy(sum, {
  apply(target, thisArg, argumentsList) {
    console.log(`调用函数,参数: ${argumentsList}`)
    return target(...argumentsList) * 2
  }
})

console.log(proxy(1, 2))  // 调用函数,参数: 1,2 \n 6
has 拦截 in 操作符
const obj = { _secret: 'secret', public: 'public' }
const proxy = new Proxy(obj, {
  has(target, key) {
    if (key[0] === '_') return false
    return key in target
  }
})

console.log('_secret' in proxy)  // false
console.log('public' in proxy)   // true

3. 其他常用陷阱方法

deleteProperty
const obj = { name: '张三', _id: 123 }
const proxy = new Proxy(obj, {
  deleteProperty(target, key) {
    if (key[0] === '_') {
      throw new Error('不能删除私有属性')
    }
    delete target[key]
    return true
  }
})

delete proxy.name  // 成功
delete proxy._id   // 报错: 不能删除私有属性
ownKeys
const obj = { name: '张三', age: 25, _secret: '123' }
const proxy = new Proxy(obj, {
  ownKeys(target) {
    return Object.keys(target).filter(key => key[0] !== '_')
  }
})

console.log(Object.keys(proxy))  // ['name', 'age']

4. 可撤销的Proxy

const target = { name: '张三' }
const { proxy, revoke } = Proxy.revocable(target, {
  get(target, key) {
    return target[key] || '默认值'
  }
})

console.log(proxy.name)  // 张三
revoke()  // 撤销代理
console.log(proxy.name)  // TypeError: Cannot perform 'get' on a proxy that has been revoked

5. 实际应用场景

  1. 数据验证
  2. 数据绑定和响应式系统(如Vue 3的响应式实现)
  3. 函数参数验证
  4. 实现私有属性
  5. 性能优化(延迟加载属性)
  6. 日志记录和调试

Proxy的这些核心用法使得它在现代JavaScript开发中非常强大,特别是在构建框架和库时