const state = {
str: 'wwming',
obj: {
y: 1
},
arr: [1, 2, 3]
}
const newState = state
console.log(newState === state) // true
newState和state是相等的
原因: 由于js的对象和数组都是引用类型。所以newState的state实际上是指向于同一块内存地址的, 所以结果是newState和state是相等的。
const state = {
str: 'wwming',
obj: {
y: 1
},
arr: [1, 2, 3]
}
const newState = state
newState.str = 'wwming is cool'
console.log(state.str, newState.str) // wwming is cool wwming is cool
可以看到,newState的修改也会引起state的修改。
如何解决?
js中提供了另一种修改数据的方式,要修改一个数据之前先制作一份数据的拷贝
,像这样
const state = {
str: 'wwming',
obj: {
y: 1
},
arr: [1, 2, 3]
}
const newState = Object.assign({}, state)
newState.str = 'wwming is cool'
console.log(state.str, newState.str)
可以使用很多方式在js中复制数据,比如:
等方式进行复制,但这些都是浅拷贝,就是只拷贝第一层数据。
更深层的数据还是同一个引用,比如:
const state = {
str: 'wwming',
obj: {
y: 1
},
arr: [1, 2, 3]
}
const newState = Object.assign({}, state)
newState.obj.y = 2
newState.arr.push(4)
console.log(state, newState)
// 执行结果
{
"str": "wwming",
"obj": {
"y": 2
},
"arr": [
1,
2,
3,
4
]
}
{
"str": "wwming",
"obj": {
"y": 2
},
"arr": [
1,
2,
3,
4
]
}
可以看到,当在更改newState更深层次的数据的时候,还是会影响到state的值。
如果要深层复制,就得一层一层的做 递归拷贝,这是一个复杂的问题。
虽然有些第三方的库已经帮我们做好了,比如lodash的cloneDeep方法。深拷贝是非常消耗性能的。
import { cloneDeep } from 'lodash'
const state = {
str: 'wwming',
obj: {
y: 1
},
arr: [1, 2, 3]
}
const newState = cloneDeep(state)
newState.obj.y = 2
newState.arr.push(4)
console.log(state, newState)
解决这个问题,就引出了 不可变数据(Immutable Data?)
Immutable data encourages pure functions (data-in, data-out) and lends itself to much simpler application development and enabling techniques from functional programming such as lazy evaluation.
– 官方文档对其描述
Immutable Data 就是 一旦创建,就不能再被更改的数据。
对 Immutable 对象的任何修改或添加删除操作都会返回一个新的 Immutable 对象。
Immutable 实现的原理是 Persistent Data Structure(持久化数据结构),也就是使用旧数据创建新数据时,要保证旧数据同时可用且不变。
同时为了避免 deepCopy 把所有节点都复制一遍带来的性能损耗,Immutable 使用了Structural Sharing(结构共享),即如果对象树中一个节点发生变化,只修改这个节点和受它影响的父节点,其它节点则进行共享。
更多精彩内容,请微信搜索“前端爱好者
“, 戳我 查看 。
----------------------- 优点 -------------------
共享的可变状态是万恶之源,举个简单的例子就是js中的引用赋值:
var obj = { a: 1 };
var copy_obj = obj;
copy_obj.a = 2;
console.log(obj.a); // 2
引用赋值虽然可以节省内存,但当应用复杂之后,可变状态往往会变成噩梦.
通常一般的做法是使用shallowCopy或者deepCopy来避免被修改,但这样造成了CPU和内存的消耗.
Immulate可以很好地解决这些问题。
上面提到了结构共享,Immutable.js 使用这种方式会尽量复用内存,甚至以前使用的对象也可以再次被复用。
没有被引用的对象会被垃圾回收。
import { Map } from 'immutable';
let a = Map({
select: 'users',
filter: Map({ name: 'Cam' })
})
let b = a.set('select', 'people');
a === b; // false
a.get('filter') === b.get('filter'); // true
上面 a 和 b 共享了没有变化的 filter 节点。
因为每次数据都是不一样的,只要把这些数据放到一个数组里储存起来,想回退到哪里就拿出对应数据即可,很容易开发出撤销重做这种功能。
Immutable(持久化数据结构)本身就是函数式编程中的概念。
函数式编程关心数据的映射,命令式编程关心解决问题的步骤,纯函数式编程比面向对象更适用于前端开发。
因为只要输入一致,输出必然一致,这样开发的组件更易于调试和组装。
----------------------- 缺点 -------------------
主要是Immutable的API设计的和原生对象类似,容易混淆操作。
例如其中Map和List的操作:
// Immutable
const map = Map({ a: 1, b: 2 });
const list = List([1,2,3]);
// 原生js
const obj = { a: 1, b: 2 };
const arry = [1,2,3];
// 取值方式对比
console.log(map.get('a'));
console.log(list.get(0));
console.log(obj.a);
console.log(arry[0]);
参考文档: