JavaScript代理与反射机制
目录- 9.1 代理基础
- 9.1.1 创建空代理
- 9.1.2 定义捕获器
- 9.1.3 捕获器参数和反射API
- 9.1.4 捕获器不变式
- 9.1.5 可撤销代理
- 9.1.6 实用 Reflect API
- 9.1.7 代理另一个代理
- 9.1.8 代理的问题与不足
- 9.2 代理捕获器与反射方法
- 9.2.1 get()
- 9.2.2 set()
- 9.2.3 has()
- 9.2.4 defineProperty()
- 9.2.5 getOwnPropertyDescriptor()
- 9.2.6 deleteProperty()
- 9.2.7 ownKeys()
- 9.2.8 getPrototypeOf()
- 9.2.9 setPrototypeOf()
- 9.2.10 isExtensible()
- 9.2.11 preventExtensions()
- 9.2.12 apply()
- 9.2.13 construct()
- 9.3 代理模式
- 9.3.1 跟踪属性访问
- 9.3.2 隐藏属性
- 9.3.3 属性验证
- 9.3.4 函数与构造函数参数验证
- 9.3.5 数据绑定与可观察对象
ES6引入的代理和反射机制为开发者提供了拦截基础操作并注入自定义行为的能力。通过创建目标对象的代理实例,可以在操作实际作用于目标前进行控制。这种机制的核心在于处理程序对象中定义的捕获器,它们能够拦截并修改对象的基本操作行为。### 9.1 代理基础
代理作为目标对象的中间层,既保持与目标的关联性,又具备独立的控制能力。所有对代理的操作都会被转发到目标对象,但可以通过捕获器实现行为拦截。
9.1.1 创建空代理
通过Proxy构造函数创建代理实例,需要指定目标对象和处理程序对象。两个参数都是必需的,缺少任一都会抛出异常。代理实例的所有操作都会映射到目标对象。
const sourceObject = {
id:'original'
};
const handlerConfig = {};
const proxyInstance = new Proxy(sourceObject, handlerConfig);
console.log(sourceObject.id); // original
console.log(proxyInstance.id); // original
sourceObject.id = 'modified';
console.log(sourceObject.id); // modified
console.log(proxyInstance.id); // modified
proxyInstance.id = 'changed';
console.log(sourceObject.id); // changed
console.log(proxyInstance.id); // changed
9.1.2 定义捕获器
捕获器是处理程序对象中定义的特殊方法,用于拦截特定操作。当通过代理执行操作时,会优先触发对应的捕获器逻辑。
const source = {
value: 42
};
const handler = {
get() {
return 'intercepted';
}
};
const proxy = new Proxy(source, handler);
console.log(source.value); // 42
console.log(proxy.value); // intercepted
9.1.3 捕获器参数和反射API
每个捕获器都接收目标对象、属性名和代理实例三个参数。通过Reflect对象可以方便地恢复原始操作行为。
const source = {
data: 'original'
};
const handler = {
get(target, prop, receiver) {
console.log(`Accessing ${prop}`);
return Reflect.get(target, prop, receiver);
}
};
const proxy = new Proxy(source, handler);
proxy.data; // Accessing data
9.1.4 捕获器不变式
捕获器行为需遵循规范限制,例如不能修改不可配置属性的访问权限:
const source = Object.freeze({ foo: 'bar' });
const handler = {
get() {
return 'modified';
}
};
const proxy = new Proxy(source, handler);
console.log(proxy.foo); // 抛出类型错误
9.1.5 可撤销代理
通过Proxy.revocable方法创建的代理支持撤销操作,撤销后所有操作都会失败:
const { proxy, revoke } = Proxy.revocable({}, {});
proxy.anyProperty; // 正常工作
revoke();
proxy.anyProperty; // 抛出错误
9.1.6 实用 Reflect API
Reflect对象提供了一系列静态方法,用于执行默认操作并返回布尔状态标识:
const obj = {};
const success = Reflect.defineProperty(obj, 'key', {
value: 'value',
enumerable: true
});
console.log(success); // true
9.1.7 代理另一个代理
代理可以嵌套使用,每个代理都会依次触发自己的捕获器:
const base = { prop: 'value' };
const firstProxy = new Proxy(base, {
get(target, prop) {
console.log('First level');
return Reflect.get(target, prop);
}
});
const secondProxy = new Proxy(firstProxy, {
get(target, prop) {
console.log('Second level');
return Reflect.get(target, prop);
}
});
secondProxy.prop; // 输出两行日志
9.1.8 代理的问题与不足
代理可能影响某些依赖对象身份判断的场景,例如WeakMap的键引用:
const wm = new WeakMap();
class Data {
constructor(id) {
wm.set(this, id);
}
}
const proxy = new Proxy(new Data(1), {});
console.log(wm.get(proxy)); // undefined
9.2 代理捕获器与反射方法
9.2.1 get()
9.2.2 set()
9.2.3 has()
9.2.4 defineProperty()
9.2.5 getOwnPropertyDescriptor()
9.2.6 deleteProperty()
9.2.7 ownKeys()
9.2.8 getPrototypeOf()
9.2.9 setPrototypeOf()
9.2.10 isExtensible()
9.2.11 preventExtensions()
9.2.12 apply()
9.2.13 construct()
9.3 代理模式
9.3.1 跟踪属性访问
通过捕获器可以记录所有属性访问和修改操作:
const target = { name: 'John' };
const proxy = new Proxy(target, {
get(target, prop) {
console.log(`Reading ${prop}`);
return Reflect.get(target, prop);
},
set(target, prop, value) {
console.log(`Updating ${prop} to ${value}`);
return Reflect.set(target, prop, value);
}
});
proxy.name; // 读取操作日志
proxy.name = 'Doe'; // 更新操作日志
9.3.2 隐藏属性
可以实现属性过滤,使特定属性不可见:
const hidden = ['secret'];
const target = { public: 'visible', secret: 'hidden' };
const proxy = new Proxy(target, {
get(target, prop) {
return hidden.includes(prop) ? undefined : Reflect.get(target, prop);
},
has(target, prop) {
return !hidden.includes(prop) && Reflect.has(target, prop);
}
});
console.log(proxy.secret); // undefined
console.log('secret' in proxy); // false
9.3.3 属性验证
可以限制属性值类型:
const target = { number: 0 };
const proxy = new Proxy(target, {
set(target, prop, value) {
if (typeof value === 'number') {
return Reflect.set(target, prop, value);
}
return false;
}
});
proxy.number = 42; // 有效
proxy.number = 'text'; // 无效
9.3.4 函数与构造函数参数验证
可以对函数参数进行类型检查:
function sum(...args) {
return args.reduce((a, b) => a + b, 0);
}
const proxy = new Proxy(sum, {
apply(target, thisArg, args) {
if (args.some(arg => typeof arg !== 'number')) {
throw new TypeError('All arguments must be numbers');
}
return Reflect.apply(target, thisArg, args);
}
});
proxy(1, 2, 3); // 有效
proxy(1, '2', 3); // 抛出错误
9.3.5 数据绑定与可观察对象
可以实现自动更新的集合管理:
const list = [];
const proxy = new Proxy(list, {
set(target, prop, value) {
const result = Reflect.set(target, prop, value);
if (result) {
console.log(`New item: ${value}`);
}
return result;
}
});
proxy.push('Alice'); // 触发日志
proxy.push('Bob'); // 再次触发日志