ITPub博客

首页 > 应用开发 > IT综合 > 不可变数据之Immutable

不可变数据之Immutable

原创 IT综合 作者:千锋Python唐小强 时间:2020-08-24 09:40:17 0 删除 编辑

在讲不可变数据(Immutable Data)前,先说说可变数据(Mutable Data),在原生js中创建的数据都是可变的,如:

    
var a = {qty:
1}

   a.qty = 10;

可能有小伙伴说,可以用const啊,const对基本数据类型还行,但对引用数据类型根本没辙,如

    
const a = {qty:
1}

   a.qty = 10;

   a.qty; // 10

如果把对象a赋值给其它变量还会导致新的问题,如:

     
const a = {qty:
1}

    const b = a;

   a.qty = 10;

   b.qty; //10

这时你会发现,修改了a,b的值也跟着改了,这其实是js采用引用赋值的方式来实现数据共享的,好处就是节省内存,但缺点也显而易见,稍微不注意就会导致改A坏B的棘手问题,在复杂的项目中,这种问题还不易排查,有诸多安全隐患。

之前的做法是,利用深拷贝的方式来解决这个问题,虽然问题解决了,但又会引发新的问题:浪费内存,还有对一些需要频繁更新数据又有高性能要求的场景(如:React),深拷贝实则为一个不明智的操作,于是,Imutable.js的出现就是要解决这些开发痛点的。

Immutable.js 由Facebook 工程师 Lee Byron 花费 3 年时间打造,在js中的引用赋值可以节省内存,但随着应用的不断复杂后,状态的改变往往会变成噩梦,通常的做法是 复制数据来避免被修改,但这样又造成了CPU和内存的消耗,而Immutable利用 结构共享可以很好地解决这些问题。

不可变数据:Immutable Data

Immutable Data 是一旦创建,就不能再被更改的数据。对 Immutable 对象的任何修改或添加删除操作都会返回一个新的 Immutable 对象。Immutable 实现的原理是 Persistent Data Structure(持久化数据结构),也就是对于不需要改变的数据采用结构共享的方式,如下图

<!-- ![持久化数据结构](./img/immutable.js结构共享.webp "持久化数据结构") -->

不可变数据之Immutable

常用数据类型

  • List: 有序索引集,类似JS中的Array。
  • Map: 无序索引集,类似JS中的Object。
  • OrderedMap: 有序的Map,根据数据的set()进行排序。
  • Set: 没有重复值的集合。
  • OrderedSet: 有序的Set,根据数据的add进行排序。
  • Stack: 有序集合,支持使用unshift()和shift()添加和删除。
  • Record: 一个用于生成Record实例的类。类似于JavaScript的Object,但是只接收特定字符串为key,具有默认值。
  • Seq: 序列,但是可能不能由具体的数据结构支持。
  • Collection: 是构建所有数据结构的基类,不可以直接构建。

正如你看到的,immutable.js的数据类型有很多,本文主要介绍比较常用的List和Map,对应于js中的数组和对象。

js与immutable之间的转换

可通过fromJS()和toJS()两个方式实现js和immuatble数据的转换,如:

    
import Immutable 
from 
'immutable';

    const goods = { name: 'huawei mate30 pro', price: 5998, brand: 'huawei'}

    // js -> immutable data
   const imData = Immutable.fromJS(goods)

   // immutable data -> js
   imData.toJS()

但fromJS()和toJS()会深度转换数据,随之带来的开销较大,尽可能避免使用,单层数据转换应直接使用Map()和List()进行转换。另外,还可以直接通过JSON.stringify()对immutable数据转换也json字符串。

    
import {
Map,List} 
from 
'immutable';


    const initState = Map({
        breadcrumb:List([]),
        user: Map({}),
        manageMenuStatus: false
   })

操作immutable数据

获取immutable中的值:get(key)/getIn(keys)

Map 和 List的通用方法,实现如下

    
import {Map,List} 
from 
'immutable';

   let state = Map({
       version: '2.0',
       user:Map({
           username: 'laoxie',
           age: 18,
           hobby:List([ '代码', '电影', '唱歌'])
       }),
   })

    // 获取 version
   state.get( 'version'); // 2.0

    // 获取username
   state.getIn([ 'user', 'username']); // laoxie

    // 获取hobby属性数据
   state.getIn([ 'user', 'hobby', 1]) // 电影

注意: 和传统的js不同,getIn()获取深层深套对象的值时不需要做每一层级的判断是否存在,如不存在则会返回undefined(JS中如果不判空会报错)

  • 添加immutable中的数据:set(key,val)/setIn(keys,val)
  • 删除属性:delete(key)/deleteIn(keys)
  • 更新属性:update(key,val=>newVal)/updateIn(keys,val=>newVal) 如开头所说的,Immutable Data为不可变数据,所有针对immutable的增删改都不会修改原数据,而是返回一个新的值,所以需要给变量重新赋值。
    
import {Map,List} 
from 
'immutable';

   let state = Map({
       version: '2.0',
       user:Map({
           id: '123',
           username: 'laoxie',
           age: 18,
           hobby:List([ '代码', '电影', '唱歌'])
       }),
   })
   state.set( 'version', '3.0');
   state.get( 'version'); //state不被修改,所以还是返回 2.0

    // 正确的修改方式:修改后重新赋值
   state = state.setIn([ 'user', 'age'], 20);
   state.getIn([ 'user', 'age']); // 20

    // update, delete操作同上
  • 判断是否存在某个属性:has(key)/hasIn(keys) 这应该也是实际开发中是比较常用的方法,通过判断属性是否存在来执行不同的操作,如可以判断user.id来判断用户是否登录
    
if(state.hasIn([
'user',
'id'])){

        // 用户已经登录
   } else{
        // 用户未登录
   }
  • 判断两个数据是否相等: is(imA,imB) 在JS中,不管是数据还是对象,通过==或===只能判断两个变量的引用地址是否为同一个对象,很难判断两个对象的键值是否相等,与JS不同,immutable是对两个对象的hashCode和valueOf进行比较的
  • 数据合并:merge()/mergeDeep() 还有一个比较常用的操作就是合并数据了,在JS我们一般使用Object.assign()来实现,但Object.assign()只能做浅合并,对层级较深的数据可以使用immutable中使用mergeDeep()来实现,两个方法都返回合并后的数据。
    const imA = Map({

        username: '马云',
        money: 150000000000,
        info:{
            married: true,
            witticism: '我没见过钱,我对钱不感兴趣'
       }
   })
   const imB = Map({
        username: 'laoxie',
        gender: '男',
        info:{
            married: false,
            age: 18,
       }
   })
   const newImData = imA.merge(imB);
   console.log(newImData.toJS());
    //输出 :
   / / {
   / /     username:'laoxie',
   / /     gender:'男',
   / /     money:150000000000,
   / /     info:{
   / /         married:false,
   / /         age:18,
   / /     }
   / / }

   const newImData = imA.mergeDeep(imB);
   / /输出 :
   / / {
   / /     username:'laoxie',
   / /     gender:'男',
   / /     money:150000000000,
   / /     info:{
   / /         married:false,
   / /         age:18,
   / /         witticism:'我没见过钱,我对钱不感兴趣'
   / /     }
   / / }

当然Immutable的方法还有很多,本文本只涉及到一引起基本操作,如果想要了解跟多数据类型的操作,请自行查看官网


来自 “ ITPUB博客 ” ,链接:https://blog.itpub.net/69923331/viewspace-2714021/ ,如需转载,请注明出处,否则将追究法律责任。

请登录后发表评论 登录
全部评论

注册时间:2019-05-14

  • 博文量
    995
  • 访问量
    775553