Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Immutable初识 #1

Open
zhaozy93 opened this issue Mar 26, 2017 · 2 comments
Open

Immutable初识 #1

zhaozy93 opened this issue Mar 26, 2017 · 2 comments

Comments

@zhaozy93
Copy link
Contributor

zhaozy93 commented Mar 26, 2017

wechatimg120

Immutable.js是facebook官方与React同期推出的JS发明之一,只是因为React太过于耀眼,Immutable的光芒被遮蔽了而已,但丝毫不耽误它的传播。
React是一个用状态state管理UI的表现层,状态state的内容、值直接决定UI的样式、表现。然而状态state的值却难以把控,众多的错误、bug、以及不理想都是由于状态state的不按预期更改。
Immutable就是用来解决这个问题的利器。

Immutable是什么

Immutable data cannot be changed once created, leading to much simpler application development, no defensive copying, and enabling advanced memoization and change detection techniques with simple logic. Persistent data presents a mutative API which does not update the data in-place, but instead always yields new updated data.

Immutable data(不可变数据)一旦创建不可以更改,用更简单的逻辑帮助实现更简单的程序开发,消除保护式复制(注:引用式复制),更高效的数据记忆和更改检测技术。Persistent data(持久性数据)提供了一种不会在原地更改数据的API,而总是返回新的更新后数据。

从facebook的官网第一段介绍文字可以看出immutable.js正是帮助我们更好的管理数据,同时极为适合使用在React state的管理当中。

约定本文中immutable即指Facebook推出的immutable.js。

痛点

在React state中,数据经常以object或array的形式存储,然后将现有数据、后台新数据、用户操作产生的数据组成下一个新的state,紧接着 setState --> shouldComponentUpdate( )。 虽然在setState只需要传入需要更新的数据,不需要传入全部state值,同时还提供了shouldComponentUpdate( ) 这样一个提高执行效率的大杀器,但使用起来并不简单。 setState前处理数据经常用到 Object.assign( )Array.concat( )Object.freeze( )等众多方法处理引用类数据,以防数据被无意覆盖或篡改。想想都累,紧接着,需要在 shouldComponentUpdate( ) 中对比当前state、propsnextstate、nextprops的值,string number boolean等值类型数据还好,可偏偏更多的数据是ArrayObject,深层对比ObjectArray便随之而来,真是不知道处理对比损耗的性能和直接忽略去渲染哪个更合适一些。。。

Immutable可以做什么

由于Immutable每次特点是每次都返回新的数据且数据生成后不可更改,即在此思路下,每次的数据更改不会影响到其与数据。也就降低了数据无意被修改、覆盖的可能性。同时配备Immutable特殊的数据hash检测机制,可以很轻易的对比两个 Object、Array数据对象。

  let obj1 = {a : 1};
  let obj2 = obj1;
  obj2.a = 5;
  console.log( obj2.a )   //5
  console.log( obj1.a )   //5

  let obj1 = {a : 1};
  let obj2 = Object.assign( {}, obj1, { a:5 });
  console.log( obj2.a )   //5
  console.log( obj1.a )   //1

看着就心累。Immutable解放了我们的天性。

  //暂时不考虑实际api
  let obj1 = Immutable.object({a : 1});   //声明一个immutable类型的object
  let obj2 = Immutable.object(obj1);     //复制一个immutable类型的object
  //数据不可变,且总是返回新数据
  obj2 = obj2.update( {a: 5} )     //对数据的更改都将返回新的数据,并不影响原数据
  console.log( obj2.a )   //5
  console.log( obj1.a )   //1

 看着还是调用了不少方法来完成这个操作,可如果习惯了之后就和let声明变量一样简单,侵入血液,岂不是就不麻烦了。而且还完成了我们的需求。

Immutable很好的帮我们解决了前面提到的一部分痛点,另一部分在后面继续介绍。

卸下Immutable面纱

刚才在上一段代码中为了便于理解,没有使用真实的api,只是模拟了一下便于书写。
上面一段代码转换为真实代码其实也很简单

  let obj1 = Immutable.Map({a : 1});   //声明一个immutable类型的object
  let obj2 = obj1.set('a', 5)    //数据不可变,且总是返回新数据
  console.log( obj2.get('a') )   //5
  console.log( obj1.get('a' ) )   //1

可以看出真实代码更简单,由于对obj1的更改不会真实作用在obj1身上,而是返回新的对象被obj2接收,代码其实少了一行。同时obj1、obj2相对独立,不会互相干扰。可能对Map还有所疑惑,在之后的post会详细解释Immutable的各种类型数据,并附有Immutable的全文文档翻译。

刚刚还提到一个Immutable利用hash来解决引用数据内部对比的问题。在Immutable内部也非常简单的实现了,Object、Array的深层对比。

  //immutable的object对比
  let obj1 = Immutable.Map( {a: Immutable.Map( {a:1} ) } ) ;
  let obj2 = Immutable.Map( {a: Immutable.Map( {a:1} ) } ) ;
  let obj3 =Immutable.Map( {a: Immutable.Map( {a:5} ) } ) ;
  Immutable.is(obj1, obj2)      //true 
  Immutable.is(obj1, obj3)      //false

  // plain js的object对比
   let a = {a:{a:{a:1}}}
   let b = {a:{a:{a:1}}}
   let c = {a:{a:{a:5}}}
   a === b       //false
   a === c       //false

在Immutable的世界里,对比一个Object与Array对象就是如此简单,是不是在shouldComponentUpdate( ) 简直好用到不行。

Immutable不止这么简单

1、Immutable帮我们降低了数据的更改风险,增加了我们对数据的可控性

2、Immutable大部分api都是以函数式编程方式实现,便于现在函数式编程的大潮流。

3、Immutable对数据的检测机制更完善,并不是无脑的深复制,而是尽可能利用现有的数据,降低了内存的开销。

4、Immutable仅仅是一种数据格式的更改,使用过程中相当于一个简单的对象(实际为类),可以与任意框架、结构、插件搭配,不会产生冲突式危害。

总结

Immutable搭配React可以极大的提高React的效率,无论从书写还是实际生产阶段。同时降低了我们对数据篡改的风险,侧方面提高了程序的健壮性。但开始使用Immutable的过程较为痛苦,容易与原生plain js的对象混淆,同时对数据操作的思路需要改变,每次都会返回新数据一定不能忘记。组件内尽量使用Immutable,但对外暴露或传递参数还是尽量使用原生js数据。

资源

camsong-Immutable 详解及 React 中实践

Sebastián Peyrott-Introduction to Immutable.js and Functional Programming Concepts

后续

Immutable的类型详细介绍

Immutable的文档翻译

@bestdqf
Copy link

bestdqf commented Jul 3, 2018

//immutable的object对比
let obj1 = Immutable.Map( {a:{a:{a:1}}} );
let obj2 = Immutable.Map( {a:{a:{a:1}}} );
let obj3 = Immutable.Map( {a:{a:{a:5}}} );
Immutable.is(obj1, obj2) //false
Immutable.is(obj1, obj3) //false

@zhaozy93
Copy link
Contributor Author

zhaozy93 commented Jul 7, 2018

@bestdqf 抱歉,之前这里确实有误。
刚刚看了一下源码, 内部对Immutable.is的实现依赖于 deepEqual 函数, 可以看得出deepEqual的原理还是对内部变量进行迭代,每次去判断是否相同,其实还是一直再循环调用is,is也就是又调用了deepEqual。
如果Immutable.Map( {a:{a:{a:1}}} ); 则第一次循环发现成员属性a就是一个普通js的obj,那么obj1与obj2的成员a的value不一致,则会返回false
更改方向:

obj1  = Immutable.Map( {a: Immutable.Map( {a:1} ) } ) 
obj2 = Immutable.Map( {a: Immutable.Map( {a:1} ) } ) 
Immutable.is(obj1, obj2) // true

deepEqual 函数

 var allEqual = true;
 var bSize = b.__iterate(function (v, k) {
    if (
      notAssociative
        ? !a.has(v)
        : flipped ? !is(v, a.get(k, NOT_SET)) : !is(a.get(k, NOT_SET), v)
    ) {
      allEqual = false;
      return false;
    }
  });

  return allEqual && a.size === bSize;

欢迎继续批评指正

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants