惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

博客园 - 三生石上(FineUI控件)
WordPress大学
WordPress大学
S
SegmentFault 最新的问题
小众软件
小众软件
T
Tailwind CSS Blog
博客园 - 聂微东
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
人人都是产品经理
人人都是产品经理
V
Visual Studio Blog
罗磊的独立博客
有赞技术团队
有赞技术团队
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
Jina AI
Jina AI
量子位
云风的 BLOG
云风的 BLOG
Recent Announcements
Recent Announcements
Hugging Face - Blog
Hugging Face - Blog
P
Proofpoint News Feed
N
Netflix TechBlog - Medium
GbyAI
GbyAI
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
腾讯CDC
美团技术团队

Coding Your Life

lib库开发的一款PDF处理小工具 | Coding Your Life mirror-cli | Coding Your Life 最长递增子序列算法 | Coding Your Life webpack5 模块联邦技术 | Coding Your Life webpack基础配置详解 | Coding Your Life canvas实现代码雨效果 | Coding Your Life 使用MessageChannel模拟React优先级执行队列 | Coding Your Life vue3 和 react 虚拟dom | Coding Your Life 前端技术分享MediaRecord实现运动相机 | Coding Your Life react基础概念 | Coding Your Life 微信小程序使用canvas创建像素头像 | Coding Your Life 前端基础概念 | Coding Your Life 中高级前端须注意的40条移动端H5坑位指南 | Coding Your Life native-code-push 热更新配置 | Coding Your Life Git 常用命令速查表 | Coding Your Life push-server 热更新常用命令速查表 | Coding Your Life 高效的js片段 | Coding Your Life
ES6中Reflect对象与Proxy结合实现代理和响应式编程 | Coding ...
Sir_Liu · 2025-05-21 · via Coding Your Life

前言

在 JavaScript 中,Reflect 是一个内置对象,提供了拦截和操作 JavaScript 对象的元方法。它是 ES6 (ES2015) 引入的特性,主要用于简化元编程(meta-programming)并与 Proxy 结合使用实现对对象属性更细粒度的操作控制。

代理对象

Proxy 的第一个参数为要进行代理的目标对象,类型为Object,如果我们代理的目标是一个基础数据类型那应该怎么实现呢?

基础数据类型的代理

在 JavaScript 中,基础数据类型(如 numberstringboolean 等)无法直接被 Proxy 代理,因为 Proxy 只能拦截对象(包括数组、函数、类等)的操作。但可以通过对象封装的方式间接代理基础类型:

1
2
3
4
5
6
7
8
9
10
11
12
13
const target  = {
value: '123'
}
const proxy = new Proxy(target, {
get (target, key, receiver){


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](但不推荐),接下来我们看下面这个例子说明不推荐的原因:

复杂数据类型的代理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
const people  = {
get name(){
console.log('thisArg:', this)
return this._name;
},
_name:'xixi'
}

const proxy1 = new Proxy(people, {
get (target, key, receiver){

console.log('get name:', target, receiver)
return Reflect.get(target, key, receiver);
},

})
console.log(proxy1.name)






const proxy2 = new Proxy(people, {
get (target, key, receiver){

console.log('get name:', target, receiver)
return target[key];
},
})
console.log(proxy2.name)




结论:通过对比输出结果,可以看出直接return target[key]会导致this无法绑定在代理对象上,当修改属性时代理事件就无法触发导致错误,所以建议直接按照Reflect.get(target, key, receiver)处理。

响应式编程

此处我们以vue3的响应式编程为例,自定义实现ref和reactive的简化版:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87


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());
}
}


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;
}
});
}


function ref(initialValue) {
const _value = { value: initialValue };


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) {

if (key === 'value' && typeof value === 'object' && value !== null) {
target[key] = reactive(value);
} else {
target[key] = value;
}
trigger(target, key);
return true;
}
});
}