在 JavaScript 中,代理(Proxy)对象和反射(Reflect)API 提供我们一个强大的能力—控制和修改对象的基本行为。它们通常用于创建各种抽象,如数据绑定、象征性(symbolic)计算。此 API 也应用于 Vue3 框架中。
使用语法:
const proxyObject = new Proxy(target, handler)
参考以下代码示例:
const P = {
a: 'a',
}
const proxyObject = new Proxy(P, {
// proxyObject.propertyName、
// Object.getOwnPropertyDescriptor(proxyObject, 'propertyName')
// 或者Reflect.get(proxyObject, 'propertyName')
// get陷阱函数会被触发。
get(target, property) {
debugger;
console.log('触发get');
return property in target ? target[property] : null
},
defineProperty(target, property, attrs) {
// 使用 Object.defineProperty(proxyObject, 'x',{}) 触发
throw new Error('不允许修改')
},
deleteProperty(target, property) {
// delete proxyObject.xx 、
// Reflect.deleteProperty(proxyObject, 'propertyName') 触发
console.log('触发deleteProperty');
delete target[property]
},
set(target, property, value) {
// proxyObject.xx 、
// Object.defineProperty()或Reflect.set() 触发
console.log('触发set');
target[property] = value
},
})
P.c = 'c' // 原对象新增一个属性
proxyObject.d = 'd' // -> 触发 set
hhh = proxyObject.a // -> 触发 get
delete proxyObject.a // -> 触发 deleteProperty
Reflect.deleteProperty(proxyObject, 'd') // -> 触发 deleteProperty
// 下方代码将抛出一个错误
// Object.defineProperty(proxyObject,'n',{
// value: 123,
// writable: false,
// enumerable: false,
// configurable: false,
// })
console.log('ooo', P)
console.log('ppp', proxyObject);
Reflect
对象主要包含以下的静态函数:
1. Reflect.apply(target, thisArgument, argumentsList)
调用一个目标函数,并指定 this
值和参数列表。
let numbers = [1, 2, 3, 4, 5]
let max = Reflect.apply(Math.max, Math, numbers)
console.log(max) // 输出 5
2. Reflect.construct(target, argumentsList[, newTarget])
等同于 new target(...args)
的操作,允许改变实例化对象时的原型。
function MyDate(...args) {
return new Date(...args)
}
let instance = Reflect.construct(MyDate, [2022, 0, 1])
console.log(instance instanceof MyDate) // Outputs: false
console.log(instance instanceof Date) // Outputs: true
3. Reflect.defineProperty(target, propertyKey, attributes)
基本等同于 Object.defineProperty
,会返回一个表示定义成功或失败的布尔值,不是抛出错误。
let obj = {}
let result = Reflect.defineProperty(obj, 'prop', { value: 1 })
console.log(result) // 输出:true
console.log(obj.prop) // 输出:1
4. Reflect.deleteProperty(target, propertyKey)
删除对象的属性,操作成功返回 true
,否则返回 false
。
let obj = { prop: 1 }
let result = Reflect.deleteProperty(obj, 'prop')
console.log(result) // 输出:true
console.log(obj.prop) // 输出:undefined
除此之外,Reflect
对象还有诸如 Reflect.get()
, Reflect.set()
, Reflect.has()
, Reflect.ownKeys()
, Reflect.isExtensible()
, Reflect.preventExtensions()
, Reflect.getPrototypeOf()
, Reflect.setPrototypeOf()
等等方法。
const data = {
name: 'John',
age: 25
};
const proxy = new Proxy(data, {
set(target, property, value) {
if (property === 'age' && typeof value !== 'number') {
throw new Error('Age must be a number');
}
target[property] = value;
return true;
}
});
proxy.age = 30; // 合法操作
console.log(proxy.age); // 输出: 30
proxy.age = 'thirty'; // 非法操作,抛出错误
const target = {
name: 'John',
age: 25
};
const handler = {
get(target, property) {
console.log(`获取属性: ${property}`);
return target[property];
},
set(target, property, value) {
console.log(`设置属性: ${property}`);
target[property] = value;
return true;
}
};
const proxy = new Proxy(target, handler);
console.log(proxy.name); // 输出: 获取属性: name, John
proxy.age = 30; // 输出: 设置属性: age
const data = {
name: 'John',
age: 25
};
const handler = {
set(target, property, value) {
target[property] = value;
console.log(`属性 ${property} 的值变为 ${value}`);
// 触发界面更新或其他逻辑
return true;
}
};
const proxy = new Proxy(data, handler);
proxy.age = 30; // 输出: 属性 age 的值变为 30
function expensiveCalculation() {
// 执行耗时的计算
console.log('执行耗时的计算');
return 100;
}
const handler = {
get(target, property) {
if (property === 'result') {
if (!target.result) {
target.result = expensiveCalculation();
}
return target.result;
}
return target[property];
}
};
const proxy = new Proxy({}, handler);
console.log(proxy.result); // 输出: 执行计算, 100
console.log(proxy.result); // 输出: 100 (缓存的结果)
Reflect
对象通常在以下两个场景下使用:
Reflect
的方法可以使你的代码更简洁、易读,因为 Reflect
的方法与 Proxy handlers 的方法一一对应。let handler = {
get(target, key) {
return Reflect.get(target, key)
}
}
let p = new Proxy({}, handler)
p.a = 1
console.log(p.a) // 输出:1
Reflect
的所有方法都是函数,它可以结合函数式编程中的各种技巧使用,如高阶函数、柯里化等。let curry = (fn, ...args1) => (...args2) => Reflect.apply(fn, null, [...args1, ...args2])
let push = curry(Array.prototype.push)
let arr = []
push(arr, 1, 2, 3)
console.log(arr) // 输出:[1, 2, 3]