实现immutable

immutable 特性

持久化数据结构、结构共享

每一个对象都是不可变的,任何的增加、修改、删除等操作都会生成一个新的对象。如果对象树中只有一个节点发生变化时,只修改改节点和受它影响的父节点,其他节点则共享。

Immutable结构共享示意图

实现简单的 immutable

核心:利用 Proxy 的特性,在外部对目标对象进行修改操作的时候,只将修改部分进行复制,其他没有修改的部分数据共享

  1. 首先定义一个类,维护数据操作的状态

根据 modified 的值判读目标对象是否被修改过,如果没有被修改则直接返回目标对象属性,否则返回拷贝后的对象属性

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
class Immutable {
modified = false; // 是否被修改
target = undefined; // 目标对象
copy = undefined; // 拷贝的对象

constructor(target){
this.target = target;
}

//如果目标对象没有被修改,直接返回原对象的某个值,否则返回拷贝对象的某个值
get(key){
if (!this.modified) return this.target[key];
return this.copy[key];
}

//如果目标对象没有被修改,对目标对象进行标记修改,否则修改拷贝的对象
set(key,value){
if(!this.modified) this.handleChanged()
return (this.copy[key] = value);
}

handleChanged(){
if(!this.modified){
this.modified = true;
//浅拷贝结构共享
this.copy = shallowCopy(this.target)
}
}
}

function shallowCopy(value){
if(Array.isArray(value)) return value.slice();
if(value.__proto__ == undefined){
return Object.assign(Object.create(null), value)
}
return Object.assign({}, value)
}
  1. 使用 Proxy 给目标对象增加代理方法,检测是否被改变,并返回相对应的结果
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
const PROXY_STATE = Symbol('state')
//对目标对象的操作行为拦截
const handler = {
get: function(target, propKey){
if(propKey === PROXY_STATE) return target
return target.get(propKey);
},
set: function (target, propKey, value) {
return target.set(propKey, value);
}
}
/**
* @param {object} target 目标对象
* @param {function} producer 对目标对象进行操作的方法
*/
function produce(target,producer){
const store = new Immutable(target);
const proxy = new Proxy(store,handler)
// 执行传入的回调方法
producer(proxy)
// 获取新的对象
const newState = proxy[PROXY_STATE]
// 查看对象是否改动
if(newState.modified) return newState.copy;
return newState.target
}
  1. 验证
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
let target = {
name:'tom',
age:18,
address:{
country:'China',
province:'Henan'
},
hobby:{
sport:['basketball','run'],
art:['draw']
}
}

const result = produce(target,res=>{
res.name='xiaoming'
})
console.log(target,result)

// {
// name: 'tom',
// age: 18,
// address: { country: 'China', province: 'Henan' },
// hobby: { sport: [ 'basketball', 'run' ], art: [ 'draw' ] }
// } {
// name: 'xiaoming',
// age: 18,
// address: { country: 'China', province: 'Henan' },
// hobby: { sport: [ 'basketball', 'run' ], art: [ 'draw' ] }
// }

console.log('1',target.address===result.address,target.hobby===result.hobby) // 1 true true

const result2 = produce(target,res=>{
res.address={
country:'China',
province:'Beijing'
}
})

console.log(target,result2)

// {
// name: 'tom',
// age: 18,
// address: { country: 'China', province: 'Henan' },
// hobby: { sport: [ 'basketball', 'run' ], art: [ 'draw' ] }
// } {
// name: 'tom',
// age: 18,
// address: { country: 'China', province: 'Beijing' },
// hobby: { sport: [ 'basketball', 'run' ], art: [ 'draw' ] }
// }

console.log('2',target.address===result2.address,target.hobby===result2.hobby) //2 false true

参考文章

深拷贝 Vs Immutable 不可变数据