ES6 Proxy 和 Reflect 用法整理

ES6 Proxy 和 Reflect 用法整理

Proxy 代理

Proxy 用于定义基本操作的自定义行为(如属性查找、赋值、枚举、函数调用等)。

基本语法

const proxy = new Proxy(target, handler)
  • target:要代理的目标对象
  • handler:包含陷阱(trap)的对象,用于定义拦截行为

常用陷阱(handler 方法)

1. 属性访问拦截

const handler = { get(target, property, receiver) { console.log(`获取属性 ${property}`); return Reflect.get(...arguments); // 默认行为 } };

2. 属性设置拦截

const handler = { set(target, property, value, receiver) { console.log(`设置属性 ${property} = ${value}`); return Reflect.set(...arguments); // 返回布尔值表示是否成功 } };

3. 删除属性拦截

const handler = { deleteProperty(target, property) { console.log(`删除属性 ${property}`); return Reflect.deleteProperty(...arguments); } };

4. 函数调用拦截

const handler = { apply(target, thisArg, argumentsList) { console.log(`函数被调用`); return Reflect.apply(...arguments); } };

5. 构造函数拦截

const handler = { construct(target, argumentsList, newTarget) { console.log(`构造函数被调用`); return Reflect.construct(...arguments); } };

6. 其他陷阱

  • has(target, prop):拦截 in 操作符
  • ownKeys(target):拦截 Object.keys() 等操作
  • getPrototypeOf(target):拦截获取原型
  • setPrototypeOf(target, proto):拦截设置原型
  • isExtensible(target):拦截 Object.isExtensible()
  • preventExtensions(target):拦截 Object.preventExtensions()
  • getOwnPropertyDescriptor(target, prop):拦截 Object.getOwnPropertyDescriptor()
  • defineProperty(target, prop, descriptor):拦截 Object.defineProperty()

完整示例

const target = { name: "target", value: 42 }; const handler = { get(target, prop, receiver) { if (prop === 'value') { return target[prop] * 2; } return Reflect.get(...arguments); }, set(target, prop, value, receiver) { if (prop === 'value') { target[prop] = value * 2; return true; } return Reflect.set(...arguments); } }; const proxy = new Proxy(target, handler); console.log(proxy.name); // "target" console.log(proxy.value); // 84 proxy.value = 10; console.log(proxy.value); // 40 (10 * 2 * 2)

Reflect 反射

Reflect 是一个内置对象,提供了拦截 JavaScript 操作的方法,与 Proxy 配合使用。

常用方法

1. 属性操作

// 获取属性 Reflect.get(target, propertyKey[, receiver]) // 设置属性 Reflect.set(target, propertyKey, value[, receiver]) // 删除属性 Reflect.deleteProperty(target, propertyKey) // 检查属性是否存在 Reflect.has(target, propertyKey)

2. 对象原型

// 获取原型 Reflect.getPrototypeOf(target) // 设置原型 Reflect.setPrototypeOf(target, prototype)

3. 对象扩展性

// 检查是否可扩展 Reflect.isExtensible(target) // 防止扩展 Reflect.preventExtensions(target)

4. 属性描述符

// 获取属性描述符 Reflect.getOwnPropertyDescriptor(target, propertyKey) // 定义属性 Reflect.defineProperty(target, propertyKey, attributes) // 获取自身属性键 Reflect.ownKeys(target)

5. 函数调用

// 调用函数 Reflect.apply(target, thisArgument, argumentsList) // 构造函数调用 Reflect.construct(target, argumentsList[, newTarget])

Reflect 与 Proxy 配合示例

const loggedObj = new Proxy({}, { get(target, name) { console.log(`get ${name}`); return Reflect.get(target, name); }, set(target, name, value) { console.log(`set ${name} = ${value}`); return Reflect.set(target, name, value); }, deleteProperty(target, name) { console.log(`delete ${name}`); return Reflect.deleteProperty(target, name); } }); loggedObj.x = 1; // set x = 1 console.log(loggedObj.x); // get x, 然后 1 delete loggedObj.x; // delete x

Proxy 和 Reflect 的实际应用

1. 观察者模式

const createObservable = (target) => { const listeners = new Set(); const handler = { set(target, prop, value) { const oldValue = target[prop]; const result = Reflect.set(target, prop, value); if (oldValue !== value) { listeners.forEach(listener => listener(prop, oldValue, value)); } return result; }, // 其他陷阱... }; const proxy = new Proxy(target, handler); return { proxy, subscribe(listener) { listeners.add(listener); return () => listeners.delete(listener); } }; }; const { proxy, subscribe } = createObservable({ count: 0 }); const unsubscribe = subscribe((prop, oldValue, newValue) => { console.log(`属性 ${prop}${oldValue} 变为 ${newValue}`); }); proxy.count++; // 属性 count 从 0 变为 1 unsubscribe();

2. 验证属性

const validator = { set(target, prop, value) { if (prop === 'age') { if (!Number.isInteger(value)) { throw new TypeError('年龄必须是整数'); } if (value < 0 || value > 150) { throw new RangeError('年龄必须在0-150之间'); } } return Reflect.set(...arguments); } }; const person = new Proxy({}, validator); person.age = 30; // 正常 person.age = '30'; // TypeError: 年龄必须是整数

3. 负索引数组

function createNegativeArrayProxy(array) { return new Proxy(array, { get(target, index) { index = Number(index); return index < 0 ? target[target.length + index] : target[index]; }, set(target, index, value) { index = Number(index); if (index < 0) { target[target.length + index] = value; } else { target[index] = value; } return true; } }); } const arr = createNegativeArrayProxy([1, 2, 3, 4]); console.log(arr[-1]); // 4 arr[-2] = 10; console.log(arr); // [1, 2, 10, 4]

注意事项

  1. Proxy 和 Reflect 是 ES6 新增的特性,旧浏览器可能需要 polyfill
  2. Proxy 可能会影响性能,因为每次操作都会经过陷阱
  3. 某些操作(如 prototype 操作)在某些情况下可能无法被 Proxy 完全拦截
  4. 使用 Reflect 可以保持默认行为,同时方便在陷阱中添加自定义逻辑

Proxy 和 Reflect 结合使用可以创建强大的元编程能力,但也要谨慎使用,避免过度复杂化代码。