前言
在 JavaScript 中,Reflect
是一个内置对象,提供了拦截和操作 JavaScript 对象的元方法。它是 ES6 (ES2015) 引入的特性,主要用于简化元编程(meta-programming)并与 Proxy 结合使用实现对对象属性更细粒度的操作控制。
代理对象
Proxy 的第一个参数为要进行代理的目标对象,类型为Object,如果我们代理的目标是一个基础数据类型那应该怎么实现呢?
基础数据类型的代理
在 JavaScript 中,基础数据类型(如 number
、string
、boolean
等)无法直接被 Proxy
代理,因为 Proxy
只能拦截对象(包括数组、函数、类等)的操作。但可以通过对象封装的方式间接代理基础类型:
const target = {
value: '123' // 这里可以是number 、string、boolean...
}
const proxy = new Proxy(target, {
get (target, key, receiver){
// receiver 指向 proxy实例
// 思考 ? 此处可否直接返回 target[key]
return Reflect.get(target, key, receiver)
},
set (target, key, value, receiver){
return Reflect.set(target, key, value, receiver)
}
})
上述代码中有一个疑问,能否通过return target[key]
取代 return Reflect.get(target, key, receiver)
? 事实上这里target[key] 等价于 Reflect.get(target, key)
, receiver
是指向代理的实例,相当于call/apply
的第一个参数,用于指定函数执行的上下文,上面例子中如果不涉及this指向,仅仅是对于简单类型的代理可使用Reflect.get(target, key) 或 target[key](但不推荐)
,接下来我们看下面这个例子说明不推荐的原因:
复杂数据类型的代理
const people = {
get name(){
console.log('thisArg:', this)
return this._name;
},
_name:'xixi'
}
const proxy1 = new Proxy(people, {
get (target, key, receiver){
// 思考 ? 此处可否直接返回 target[key]
console.log('get name:', target, receiver)
return Reflect.get(target, key, receiver);
},
})
console.log(proxy1.name)
// 输出:
// get name: { name: [Getter], _name: 'xixi' } name { name: [Getter], _name: 'xixi' }
// thisArg: { name: [Getter], _name: 'xixi' }
// get name: { name: [Getter], _name: 'xixi' } _name { name: [Getter], _name: 'xixi' }
// xixi
const proxy2 = new Proxy(people, {
get (target, key, receiver){
// 思考 ? 此处可否直接返回 target[key]
console.log('get name:', target, receiver)
return target[key];
},
})
console.log(proxy2.name)
// 输出:
// get name: { name: [Getter], _name: 'xixi' } name { name: [Getter], _name: 'xixi' }
// thisArg: { name: [Getter], _name: 'xixi' }
// xixi
结论:通过对比输出结果,可以看出直接return target[key]
会导致this
无法绑定在代理对象上,当修改属性时代理事件就无法触发导致错误,所以建议直接按照Reflect.get(target, key, receiver)
处理。
响应式编程
此处我们以vue3
的响应式编程为例,自定义实现ref和reactive的简化版:
// 存储依赖关系的 WeakMap
const targetMap = new WeakMap();
// 当前正在收集依赖的副作用函数
let activeEffect = null;
// 副作用函数
function effect(fn) {
activeEffect = fn;
fn(); // 执行副作用,触发依赖收集
activeEffect = null; // 清空
}
// 依赖收集函数
function track(target, key) {
if (activeEffect) {
let depsMap = targetMap.get(target);
if (!depsMap) {
targetMap.set(target, (depsMap = new Map()));
}
let dep = depsMap.get(key);
if (!dep) {
depsMap.set(key, (dep = new Set()));
}
dep.add(activeEffect); // 将副作用添加到依赖集合
}
}
// 触发依赖更新
function trigger(target, key) {
const depsMap = targetMap.get(target);
if (!depsMap) return;
const dep = depsMap.get(key);
if (dep) {
dep.forEach(effect => effect()); // 执行所有依赖副作用
}
}
// 简化版 reactive
function reactive(target) {
return new Proxy(target, {
get(target, key, receiver) {
const result = Reflect.get(target, key, receiver);
track(target, key); // 收集依赖
return result;
},
set(target, key, value, receiver) {
const oldValue = target[key];
const result = Reflect.set(target, key, value, receiver);
if (oldValue !== value) {
trigger(target, key); // 触发更新
}
return result;
}
});
}
// 简化版 ref
function ref(initialValue) {
const _value = { value: initialValue }; // 创建一个包裹对象
// 对于对象类型,使用 reactive 转换为响应式
if (typeof initialValue === 'object' && initialValue !== null) {
_value.value = reactive(initialValue);
}
return new Proxy(_value, {
get(target, key) {
track(target, key); // 收集依赖
return target[key];
},
set(target, key, value) {
// 对于对象类型,使用 reactive 转换
if (key === 'value' && typeof value === 'object' && value !== null) {
target[key] = reactive(value);
} else {
target[key] = value;
}
trigger(target, key); // 触发更新
return true;
}
});
}