immutable入坑指南

综合技术 2018-02-08

本文作者:冬柠

前言

今天在用 immutable 改写以前的旧代码。数据的嵌套结构有可能很深的情况, 内部对于数据的增删改查和各种循环递归操作有很多。用了 immutable.js (其实也配合使用了TypeScript,效果更佳) 来改造了下。一个总结体验那就是 ~

immutable初相识

immutable 的英文意思是“不可变的”。从名字就已经体现出 immutable.js 的精髓了。在immutable的世界观里, 一切都是不可变的数据。对 Immutable 数据的修改会返回一个新的 Immutable 对象。

而传统的Js里十分让人头疼的一点就是对象的数据传递是引用传递, 更新了新的对象也会影响原有的对象。从而使用各种 deepExtend, deepCopy方法来解决这个问题。而使用immutable.js就没有这个烦恼。

举个栗子:

let obj1 = Immutable.Map({ a: 1, b: 2, c: 3 })
let obj2 = obj1
obj2 === obj1
// return true(此时obj2实例和obj1实例指向了同一个地址)
obj2 = obj2.set("b",4)
// 此时obj2已经被更新,指向了另一个地址
obj2 === obj1
// return false
// 改变obj2并不影响obj1

常用的对象

  • Map : 键值对结构,通常对应js中的object结构转换过来的类型。但是和js的object的很多特性有明显的区分。
  • Object只接受字符串作为键名,然而Map接受任何类型的数据作为键名。
let obj = { 1: "one" }
Object.keys(obj) // [ "1" ]
console.log(obj["1"], obj[1]) // "one","one"
let map = Map(obj)
console.log(map.get("1"), map.get(1)) // "one", undefined
  • Map支持键值对的遍历( map,forEach,filter )等操作。大大方便了js中 Object.keys() 来遍历对象的写法
  • OrderedMap: 一种有序的Map类型。弥补了Object对象无序的缺憾。
  • List: 有序列表, 对应js中的Array
  • Set: 不可重复的列表

常用的操作

  • 和js数据类型相互转化
  • Immutable.Map({}) 对象换成Map
  • Immutable.List([]) 数组换成List
  • Immutable.formJS()
  • 识别传入的数据并自动转换成相应 Map | List 类型
  • 对于Immutable对象, 有 toJS() , toJSON() , toArray() , toObject() 方法转换成相应的原生Js结构。
  • 判断两个对象的值是否相等
let obj1 = Immutable.Map({ a: 1, b: 2, c: 3 })
let obj2 = Immutable.Map({ a: 1, b: 2, c: 3 })
Immutable.is(obj1, obj2); // or obj1.equals(obj2)
// return ture
  • 深度取值
let map1 = Immutable.Map({ a: {d: { target : 1}}, b: 2, c: 3 })
const target = Immutable.getIn(map1, ["a","d","target"])
// target: 1
  • 深度赋值

    有一个 cursor (游标)的概念, 可以进行深度赋值的操作。但会在下一个版本即将被废弃。建议使用 setIn 来代替。

let map1 = Immutable.Map({ a: {d: { target : 1}}, b: 2, c: 3 })
map1 = Immutable.setIn(map1, ["a","d","target"], 4)
// map1: { a: {d: { target : 4}}, b: 2, c: 3 }
  • 浅合并(类似于Object.assign, 只合并了第一层)
const map1 = Immutable.Map({ a: {d: { target : 1}}});
const map2 = Immutable.Map({ a: {c: 1}});
const map3 = Immutable.merge(map1,map2)
console.log(map3.toJS())
// { a: {c: 1}}
  • 深度合并
const map1 = Immutable.Map({ a: {d: { target : 1}}});
const map2 = Immutable.Map({ a: {c: 1}});
const map3 = Immutable.mergeDeep(map1,map2)
console.log(map3.toJS())
// { a: {c: 1, d: { target : 1}}}}

优势

  • 节省内存
  • 使用了 Trie数据结构 来进行储存。使用了结构共享,即如果对象树中一个节点发生变化,只修改这个节点和受它影响的父节点,其它节点则进行共享。
  • 比如对象
const data = {
to: 7,
tea: 3,
ted: 4,
ten: 12,
A: 15,
i: 11,
in: 5,
inn: 9
}

根据trie结构,存储的结构类似于

如果更改了了tea字段 3 -> 14,那么只需改变四个节点,来更新形成新的树, 这就是结构共享。

  • 提升计算性能
  • 使用了 Trie数据结构。避免深度比较。如果两个对象的值相等。那么他们的hashCode也相等。从而比较hashCode即可比较两个对象的值是否相等,避免了深度比较。
  • 拥抱函数式编程
  • 使用便利
  • 因为数据是不可变的。同时提供的API面向开发者也十分友好。

使用immutable的注意事项

  • 变量命名
  • 非常值得注意的一个点。因为可能引用了其他的库或者文件,使得代码里存在immutable数据和非immutable数据。所以immutable变量需要加上 $$ 前缀来区分
  • API上的习惯
  • 赋值操作之后要修改原数据记得要赋值。不然无法更新数据。
    $$data = $$data.set("hello","immutable")
  • 无法使用function传值来修改数据
let $$data = Map({hello: "immutable"});
function changeData($$data){
$$data = $$data.set("hello","world")
}
changeData($$data)
console.log($$data.toJS())
// {hello: "immutable"}

如上, $$data并不会改变。所以在写改变数据的方法的时候需要返回新值。然后覆盖旧值。

let $$data = Map({hello: "immutable"});
function changeData($$data){
return $$data.set("hello","world")
}
$$data = changeData($$data)
console.log($$data.toJS())
// {hello: "world"}
  • 为了性能和节省内存, Immutable.js 会努力避免创建新的对象。如果没有数据变化发生的话。
const { Map } = require('immutable')
const originalMap = Map({ a: 1, b: 2, c: 3 })
const updatedMap = originalMap.set('b', 2)
updatedMap === originalMap // return ture

如上代码虽然进行了一顿操作。然而数据并没有改变。所以updatedMap和originalMap还是指向了同一个对象。

const updatedMap = originalMap.set('b', 4)
updatedMap === originalMap
// return false

此时返回false

  • Immutable的使用场景

    虽然immutable有种种好处, 但并不是在所有场景都建议使用Immutable, 对于数据结构复杂,操作很多的使用immutable比较合适。但对于简单的应用场景, 还是使用原生的js吧。

您可能感兴趣的

使用immutable优化React 文章同步于Github Pines-Cheng/blog React在减少重复渲染方面确实是有一套独特的处理办法,那就是虚拟DOM,但显然在首次渲染的时候React绝无可能超越原生的速度,或者一定能将其它的框架比下去。尤其是在优化前的React,每次数据变动都会执行render,大大影响了性...
Are your strings immutable? A value is immutable if it cannot change. Immutability is a distinct notion than that of a constant . The speed of light in a vacuum is be...
Immutable npm install immutable immutable 可以基于共享部分对象来创建新的对象 :可以理解为两个对象,相同的地方引用的都是同一部分,是相同的。不同的地方是不同的。 let { Map } = require('immutable'); let m1 = Map({ a: ...
Better Code Disclaimer : everything in this post is my own opinion. However, it’s 100% true and you should not question it. Let’s get started. ...
How Custom Assertion Matchers will keep mocks away I cannot write a series about avoiding mocks without mentioning Custom Assertion Matchers. If you don’t know what custom assertions are, here is p...