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吧。

责编内容by:阿里巴巴国际UED 【阅读原文】。感谢您的支持!

您可能感兴趣的

Scale up your parallel R workloads with containers... by JS Tan (Program Manager, Microsoft) The R language is by and far the most po...
Facebook 重新修改 React, Jest, Flow 和 Immutable.js 授权协... 简评:号外!号外!Facebook 将 React, Jest, Flow 和 Immutable.js 的授权协议修改为了 MIT 。 从上...
Learn these JavaScript fundamentals and become a b... JavaScript has primitives, objects and functions. All of them are values. All...
深浅拷贝与react之immutable.js 深拷贝和浅拷贝只针对像Object,Array这样的复杂对象,对于基本的数据类型,则不存在深拷贝和浅拷贝的区别。浅拷贝只是复制了对象的一层属性,而深拷贝则是递归...
Using Immutable.js with React and Redux A guide on when to use Immutable.js and how to use it with Redux React and Red...