什么是Proxy?常见使用场景有哪些?

1. 什么是 Proxy

Proxy 用于定义基本操作的自定义行为。

本质:修改程序的默认行为,就如同在编程语言层面上做修改,属于元编程(meta programming)。

元编程(Metaprogramming,又译超编程)是指某类计算机程序的编写,这类程序编写或者操纵其它程序(或者自身)作为它们的数据,或者在运行时完成部分本应在编译时完成的工作。

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

2. 用法

如果目标对象自身的某个属性不可写且不可配置,那么 set 方法将不起作用:

const obj = {};
Object.defineProperty(obj, 'foo', { value: 'bar', writable: false });

const handler = {
  set(obj, prop, value) { obj[prop] = 'baz'; }
};

const proxy = new Proxy(obj, handler);
proxy.foo = 'baz';
proxy.foo; // "bar"

如果属性不可配置(configurable)且不可写(writable),则 Proxy 不能修改该属性,否则会报错:

const target = Object.defineProperties({}, {
  foo: { value: 123, writable: false, configurable: false }
});
const handler = {
  get(target, propKey) { return 'abc'; }
};
const proxy = new Proxy(target, handler);
proxy.foo;
// TypeError: Invariant check failed

取消代理

const { proxy, revoke } = Proxy.revocable(target, handler);
revoke(); // 之后访问 proxy 会抛 TypeError

3. 使用场景

场景一:访问/修改受保护字段

let api = {
  _apiKey: '123abc456def',
  getUsers() {},
  getUser(userId) {},
  setUser(userId, config) {}
};
const RESTRICTED = ['_apiKey'];

api = new Proxy(api, {
  get(target, key, proxy) {
    if (RESTRICTED.indexOf(key) > -1) throw Error(`${key} 不可访问.`);
    return Reflect.get(target, key, proxy);
  },
  set(target, key, value, proxy) {
    if (RESTRICTED.indexOf(key) > -1) throw Error(`${key} 不可修改`);
    return Reflect.set(target, key, value, proxy);
  }
});

console.log(api._apiKey);   // 报错
api._apiKey = '987654321'; // 报错

场景二:观察者模式

观察者模式(Observer mode)指的是函数自动观察数据对象,一旦对象有变化,函数就会自动执行。

observable 函数返回一个原始对象的 Proxy 代理,拦截赋值操作,触发充当观察者的各个函数:

const queuedObservers = new Set();
const observe = fn => queuedObservers.add(fn);

const observable = obj => new Proxy(obj, {
  set(target, key, value, receiver) {
    const result = Reflect.set(target, key, value, receiver);
    queuedObservers.forEach(observer => observer());
    return result;
  }
});

const obj = observable({ count: 0 });
observe(() => console.log('changed:', obj.count));
obj.count = 1; // 自动触发观察者

补充:Proxy 的常见用途:

  • 数据校验(如 Vue 3 的响应式系统就是基于 Proxy)。
  • 访问控制(私有字段保护)。
  • 日志/埋点(自动记录属性读写)。
  • 缓存 / lazy load(get 时才去取值)。
  • 观察者/响应式:自动通知订阅者。
  • 与 Reflect 配合使用更规范:Reflect.get / Reflect.set 等。

来源整理自:我的有道云笔记