前言
自从vue的出现,让很多前端开发对defineProperty这个方法进入了深入的了解,因为它涉及到vue核心实现,视图与数据的双向绑定,然而vue3后又盯上了Proxy,它比defineProperty完美,很适合做双向绑定。
defineProperty
ECMAS-262第5版在定义只有内部采用的特性时,提供了描述了属性特征的几种属性。ECMAScript对象中目前存在的属性描述符主要有两种,数据描述符(数据属性)和存取描述符(访问器属性),数据描述符是一个拥有可写或不可写值的属性。存取描述符是由一对 getter-setter 函数功能来描述的属性。
defineProperty提供了对数据描述属性进行修改的操作,而这也提供更加灵活操作,与在getter-setter 函数功能进行某些搜集和变化,所以这也让双向绑定变得更加简单可控。
以下为数据双向绑定例子,但没有对视图与数据进行绑定,只是提供了一个思路。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
88
89
90
91
92
93
94
95
96
97
98// 校验是否是对象
function isObject(obj){
return Object.prototype.toString.call(obj) === '[object Object]';
}
// 循环递归给每个对象的值设置defineProperty
function defineReactive(obj, key, val){
if(isObject(val)){
observe(val);
}else if(Array.isArray(val)){
observeArray(val);
dealAugment(val);
}
Object.defineProperty(obj, key, {
enumerable: true,
configurable: true,
get(){
// 处理获取数据
console.log(`获取值:${val}`);
return val;
},
set(newVal){
// 处理设置数据
console.log(`设置值:newVal-${newVal},oldVal-${val}`);
val = newVal;
if(isObject(newVal)){
observe(newVal);
}else if(Array.isArray(val)){
observeArray(val);
dealAugment(val);
}
}
});
}
// 数组变异方式实现
function dealAugment(arr){
// 把数组的所有原型方法赋值给arrysMethod
const arrysMethod = Object.create(Array.prototype);
// 重构push,就是变异psuh方法,这里可以实现其他的变异,如pop、shift、unshift
arrysMethod.push = function(...args){
// 处理push方法数据
console.log("push:",args);
[].push.apply(arr, args);
}
// 重新把原型赋值到数组里
if("__proto__" in Object){
arr.__proto__ = arrysMethod;
}else{
// 不支持__proto__,变异当前方法
Object.defineProperty(arr, "push", {
value: arrysMethod.push,
enumerable: true
})
}
}
// 数组部分遍历递归检测对象与数组,实现defineProperty与数组变异
function observeArray(arr){
arr.forEach((item)=>{
if(isObject(item)){
observe(item);
}else if(Array.isArray(item)){
observeArray(item);
dealAugment(item);
}
});
}
// 遍历对象,对象值设置defineProperty
function observe(obj){
Object.keys(obj).forEach((key)=>{
defineReactive(obj, key, obj[key]);
});
}
// 测试部分
setTimeout(()=>{
let obj = {
a: "1",
b: {
c: "xxx"
},
c: [12,1],
d: [[1212],["ccc"],"vvv",{e:"222"}]
};
window.obj = obj;
observe(obj);
console.log(obj.a);
obj.a = {v:1};
obj.b.c = "wqss";
obj.c.push(10);
obj.c.push([1000]);
obj.d[0].push("vvv");
obj.d[3].e = "xcsss";
},1000);
Proxy
Proxy比defineProperty完善,因为Proxy相当于实现了一个代理,让数据都经过代理里的设置后再输出,所以我们可以再代理里的钩子里拦截下数据,进行操作后进行下一步的数据变化,而且也不需要数组方法变异之类的,因为只要代理的数据有变化,就需要经过代理里的钩子,从而不需要编写一些hack代码。
1 | const handler = { |