人人都会AI编程

代理拦截的 13 种操作

更新时间:2026-07-11

ES6 引入的 Proxy 提供了一种元编程能力——可以自定义对象的基本操作行为。通过 new Proxy(target, handler),你可以拦截并重新定义目标对象上的 13 种内部方法,覆盖了对象几乎一切可能的行为。下面逐一拆解这 13 种拦截操作,附上实用场景和简洁示例。

1. get(target, property, receiver)

拦截属性读取,包括普通属性、方法、原型链上的属性。

const user = { name: 'Alice' };
const proxy = new Proxy(user, {
  get(target, prop) {
    if (prop in target) {
      return target[prop];
    }
    return '属性不存在';
  }
});
console.log(proxy.name); // 'Alice'
console.log(proxy.age);  // '属性不存在'

实用场景

  • 默认值设置、属性不存在时的友好提示。
  • 对象懒加载(如大型数据按需获取)。
  • 访问日志记录(AOP)。

2. set(target, property, value, receiver)

拦截属性赋值

const proxy = new Proxy({}, {
  set(target, prop, value) {
    if (typeof value === 'number') {
      target[prop] = value;
      return true; // 赋值成功
    }
    console.warn('只允许数值类型');
    return false; // 拒绝赋值
  }
});
proxy.age = 25;   // 成功
proxy.name = 'A'; // 警告,赋值失败

实用场景

  • 数据验证与类型检查。
  • 双向绑定(如 Vue 的响应式原理底层)。
  • 禁止修改只读对象。

3. has(target, property)

拦截 in 操作符。

const proxy = new Proxy({ secret: 1 }, {
  has(target, prop) {
    if (prop === 'secret') return false;
    return prop in target;
  }
});
console.log('secret' in proxy); // false
console.log('name' in proxy);   // false

实用场景

  • 隐藏某些私有属性。
  • 扩展 in 操作的行为(如数组“元素存在”判断)。

4. deleteProperty(target, property)

拦截 delete 操作。

const proxy = new Proxy({ id: 1 }, {
  deleteProperty(target, prop) {
    if (prop === 'id') {
      console.log('id 属性不可删除');
      return false;
    }
    delete target[prop];
    return true;
  }
});
delete proxy.id; // 被阻止

实用场景

  • 保护关键属性不被删除。
  • 资源清理(如自动断开引用、销毁关联对象)。

5. ownKeys(target)

拦截以下方法:Object.keys()Object.getOwnPropertyNames()Object.getOwnPropertySymbols(),以及 for...in(会触发 ownKeysgetOwnPropertyDescriptor)。

const proxy = new Proxy({ a: 1, b: 2 }, {
  ownKeys(target) {
    return ['a']; // 只暴露 a
  }
});
console.log(Object.keys(proxy)); // ['a']

实用场景

  • 限定可枚举属性,实现“白名单”视图。
  • 性能优化(减少不必要的迭代)。

6. getOwnPropertyDescriptor(target, property)

拦截 Object.getOwnPropertyDescriptor()

const proxy = new Proxy({}, {
  getOwnPropertyDescriptor(target, prop) {
    if (prop === 'x') {
      return { value: 1, writable: false, enumerable: true, configurable: false };
    }
    return undefined;
  }
});

实用场景

  • 动态生成属性特性(如不可写、不可配置)。
  • Proxy 配合创建“虚拟属性”。

7. defineProperty(target, property, descriptor)

拦截 Object.defineProperty()Object.defineProperties()

const proxy = new Proxy({}, {
  defineProperty(target, prop, descriptor) {
    if (prop.startsWith('_')) {
      return false; // 不允许定义以下划线开头的属性
    }
    Object.defineProperty(target, prop, descriptor);
    return true;
  }
});

实用场景

  • 限制属性创建规则。
  • 同步更新外部状态(如在 Vue 中触发重新渲染)。

8. preventExtensions(target)

拦截 Object.preventExtensions()

const proxy = new Proxy({}, {
  preventExtensions(target) {
    console.log('禁止扩展');
    Object.preventExtensions(target);
    return true;
  }
});

实用场景

  • 日志或权限检查,当对象被“封印”时发出通知。

9. isExtensible(target)

拦截 Object.isExtensible()

const proxy = new Proxy({}, {
  isExtensible(target) {
    return false; // 伪装成不可扩展,但不影响实际扩展行为(可能导致不一致)
  }
});

注意:此方法返回的值必须与实际目标状态一致,否则会抛出类型错误。通常不单独使用。

10. getPrototypeOf(target)

拦截 Object.getPrototypeOf()

const proxy = new Proxy({}, {
  getPrototypeOf(target) {
    return Array.prototype; // 返回任意原型
  }
});

实用场景

  • 隐藏真实的原型链。
  • 虚拟化对象原型(例如模拟继承关系)。

11. setPrototypeOf(target, prototype)

拦截 Object.setPrototypeOf()

const proxy = new Proxy({}, {
  setPrototypeOf(target, proto) {
    // 禁止修改原型
    return false;
  }
});

实用场景

  • 锁定原型,防止运行时篡改。

12. apply(target, thisArg, argumentsList)

拦截函数调用。该拦截器只对函数对象有效。

const double = n => n * 2;
const proxy = new Proxy(double, {
  apply(target, thisArg, args) {
    console.log(`调用参数: ${args}`);
    return target(...args) * 2; // 结果加倍
  }
});
console.log(proxy(5)); // 20(5*2*2)

实用场景

  • 函数性能监控(记录调用次数、耗时)。
  • 参数校验与转换。
  • AOP 埋点(统一日志、异常处理)。

13. construct(target, argumentsList, newTarget)

拦截 new 操作,必须返回一个对象。

function Person(name) {
  this.name = name;
}
const ProxyPerson = new Proxy(Person, {
  construct(target, args) {
    console.log(`使用参数 ${args} 构造`);
    return new target(...args);
  }
});
const p = new ProxyPerson('Alice'); // 触发拦截

实用场景

  • 单例模式:确保 new 只返回同一个实例。
  • 构造函数参数预处理。
  • 统计对象创建次数。

真实世界的应用:响应式系统雏形

Proxy 的 13 种拦截器不是孤立的,它们可以在 handler 中组合使用。最常见的组合就是构建响应式系统(如 Vue 3 的 reactive() 核心):

function reactive(obj) {
  return new Proxy(obj, {
    get(target, key) {
      // 依赖收集……
      return target[key];
    },
    set(target, key, val) {
      target[key] = val;
      // 触发更新……
      return true;
    }
  });
}

借助 getset 即可追踪数据读写,不再需要像 Object.defineProperty 那样递归劫持所有属性。而且 Proxy 还能直接监听数组索引变化、对象属性增删(deleteProperty),这是早期响应式实现无法做到的。

Object.defineProperty 的对比优势

| 方面 | Object.defineProperty | Proxy |
|------|-----------------------|---------|
| 拦截粒度 | 只能劫持现有属性 | 代理整个对象,监听新增/删除属性 |
| 数组监听 | 需要覆盖数组方法 | 直接监听索引和 length 的变化 |
| 性能 | 需递归为每个属性设置 getter/setter | 只需一层代理,懒处理 |
| 支持的拦截类型 | 仅 get/set | 13 种操作全覆盖 |

正是这些优势,使 Proxy 成为现代前端框架实现数据绑定、不可变数据、虚拟化对象的底层支柱。掌握这 13 种拦截操作,你就多了一把解锁高阶编程范式的钥匙。