前言
在javascript中,对象之间的相互赋值,一般都是对内存地址引用的赋值,也就是不同的变量名可能指向同一块内存区域。
这样的优点就是可以节省内存空间,但是一旦项目趋于复杂,给我们带来的弊端远远大于它的优点。因此,大部分情况下,我们可能会采用深拷贝或者浅拷贝的方式给一个变量赋值,然后改变其内容,但同样这样又会导致内存和性能的浪费。
Immutable.js
通过Immutable.js创建的数据我们称为Immutable Data,Immutable Data一旦创建,就不可再更改,对其进行的添加,修改,删除操作都会生成一个新的Immutable Data。Immutable保证了就数据的可用且不变,同时为了避免深浅拷贝对性能消耗,immatable采用了结构共享,也就是对对象树的一个节点进行修改,只会修改该节点以及该节点的父级,其他节点则进行共享。
Immutable的优缺点
优点
1、降低了可变带来的复杂度
function testFn(){
let data={type:'string'};
someFn(data);
console.log(data)
}
在不查看someFn的代码时,我们是不会知道someFn内部会对data对什么处理,但是如果data是个immutableData的话,那会很明显,data的值是不会发生变化。
2、节省内存
immutable使用结构共享时会尽量做到内存共享,比如下面代码:
import {Map} from 'immutable'
let a=Map({
name:'testname',
list:[1,23,4]
});
let b=a.set('name','changeName');
console.log(a===b);//false
console.log(a.get('list')===b.get('list'));//true
3、开发撤销回退功能将会很简单
由于每次修改数据,都会生成一个不同的对象。只需要将每一步操作后的数据存起来,回退的时候取出即可。
缺点
immutable的最大问题是比较容易与js的原生对象混淆,同时许多操作都与js中的处理原生对象的不同,例如immutable中使用map,list分别表示object和array,而在map中获取到值,需要使用
map.get(key)
设定值,需要使用
map.set(key,value)
并且每次修改时都会生成新的对象,按照js的习惯,可能会忘记赋值操作。另外有些第三方库需要使用js的原生对象,可能会忘记进行类型转换。
Immutable.is
两个immutable对象就算值完全一样,存放的内存地址也不一样,因此使用===对比时仍然会输出false。可以使用Immutable.is方法对比。
import {Map,is} from 'immutable'
let a=Map({key1:'1',key2:'2'});
let b=Map({key1:'1',key2:'2'});
console.log(a===b); //false
console.log(is(a,b)); //true
immutable.is比较的是两个对象hashCode或valueOf(javascript对象),由于immutable对象内部采用trie数据结构存储数据,因此只要hashCode一样,对象的值就一样。这样就不需要深度比较,节省了性能。
与react中的shouldComponentUpdate相结合
react中的shouldComponentUpdate生命周期,用来判断是否重新渲染组件,默认都会返回true。我们可以在shouldComponentUpdate周期中利用Immutable.is和===判断是否触发重新render。
代码可以如下
import React, {Component} from 'react';
import {Map,is} from 'immutable'
class App extends Component{
constructor(props){
super(props);
}
shouldComponentUpdate(newProps,newState){
let thisProps=this.props||{},
thisState=this.state||{};
if(Object.keys(newProps).length!==Object.keys(thisProps).length||
Object.keys(newState).length!==Object.keys(thisState).length
){
return true
}
//forin循环会遍历出原型链中的属性,因此最好加一个hasOwnProperty判断
for(const key in newProps){
if(newProps.hasOwnProperty(key)&&!is(newProps[key],thisProps[key])){
return true
}
}
for(const key in newState){
if(newState.hasOwnProperty(key)&&!is(newState[key],thisState[key])){
return true
}
}
return false
}
render(){
return (
<div>
<span>{text}</span>
</div>
)
}
}
export default App
其实为什么不直接这么判断?
if(!is(newProps,thisProps)||!is(newState,thisState)){
return true
}
实际上,immutable.is同样对深层次数据的判断还是有些乏力
比如
let obj1={key1:'1',key2:'2',key3:'3',arr:[1,2,3]};
let obj2={key1:'1',key2:'2',key3:'3',arr:[1,2,3]};
let a=Map(obj1);
let b=Map(obj2);
console.log(is(a,b));//false
只有引用的对象,指向同一个内存地址,才会为true
let arr=[1,2,3];
let obj1={key1:'1',key2:'2',key3:'3',arr};
let obj2={key1:'1',key2:'2',key3:'3',arr};
let a=Map(obj1);
let b=Map(obj2);
console.log(is(a,b));//true