本文只是我的在熟悉Immutable.js
的一些我的筆記,所以我只根據我本身的狀況來熟悉API
,因此不少API
並無被列舉到,好比常規的push/map/filter/reduce
等等操做,這些API
我認爲只要你本身稍微看一下官網的介紹均可以知道怎麼用。本文全部的代碼請參看本人的github
地址https://github.com/Rynxiao/immutable-learn。javascript
Immutable data cannot be changed once created . Persistent data presents a mutative API which does not update the data in-place, but instead always yields new updated data.html
Immutable.js provides many Persistent Immutable data structures including:
List
,Stack
,Map
,OrderedMap
,Set
,OrderedSet
andRecord
.javaThese data structures are highly efficient on modern JavaScript VMs .git
Keywords:cannot be changed,yields new updated data,efficientgithub
Immutable數據結構一旦被建立就不會被修改,每次API的操做都會在此數據之上另外返回一個新的數據。同時他自身的API中提供了不少咱們平時在工做中可能用到的數據結構,例如:List,Stack,Map...。算法
npm install immutable
調用:npm
const { Map } = require('immutable'); const map = Map({ a: 1, b: 2, c: 3 }); map.get('a'); // 1
下載immutable.min.js,放在本身項目庫文件中,而後引用:後端
<script src="immutable.min.js"></script> <script> var map = Immutable.Map({ a: 1, b: 2, c: 3 }); map.get('a'); // 1 </script>
Immutable中的Collection是一個基類,放在後端語言Java中來講就是一個抽象類,其中定義了不少方法,提供給子類來實現。所以Collection不能直接使用其構造函數。其中它又分紅了幾個子類,分別是 Collection.Keyed
,Collection.Indexed
, or Collection.Set
,List
/Map
/Set
/Stack
分別都是繼承他們而來。api
提供的基本操做 (例如:get/set/update/map/filter之類的) 這裏將不會被講到,想要了解的能夠具體去官網看API。數組
這裏講下equals
和hashCode
方法,這在javascript的幾種數據中都不存在,是Immutable數據中特有的兩個方法,用來判斷兩個數據的值是否相等。這裏強調了"值"的概念。在Immutable中,全部的數據都是以values
(值)的方式體現的。若是一個數據結構中,equals
與hashCode
方法返回的值相同,那麼Immutable即認爲它們值相等。這也是在Immutable中的is
方法中有體現。
Also, an important relationship between these methods must be upheld: if two values are equal, they must return the same hashCode. If the values are not equal, they might have the same hashCode; this is called a hash collision,
看到下面幾行:
// src/is.js export function is(valueA, valueB) { //... return !!(isValueObject(valueA) && isValueObject(valueB) && valueA.equals(valueB)); } // src/Predicates.js export function isValueObject(maybeValue) { return !!(maybeValue && typeof maybeValue.equals === 'function' && typeof maybeVaule.hashCode === 'function'); } // src/CollectionImpl.js 315行 equals(other) { return deepEqual(this, other); } // src/utils/deepEqual.js export default function deepEqual(a, b) { // 這裏的算法略,若是感興趣看是如何比較的能夠本身去看 // 這裏主要看一個關鍵詞 // ... if ( !isCollection(b) || (a.size !== undefined && b.size !== undefined && a.size !== b.size) || (a.__hash !== undefined && b.__hash !== undefined && a.__hash !== b.__hash) // ... ) { return false; } // 這裏看到 __hash 這個屬性從哪來,所以咱們這回去看 } // src/CollectionImpl.js 532行 hashCode() { return this.__hash || (this.__hash = hashCollection(this)); }
因此,這裏暴露了一些信息:使用is
函數,須要比較hash值是否相等,那麼用到hash值就必須調用hashCode
函數,而後再進行具體值得比較,就會調用equals
方法。
下面,咱們來看幾個is
方法的例子:
const Immutable = require('./lib/immutable.js'); let a = 1; let b = '1'; let c = 1; let d = { a: 1 }; let e = { a: 1 }; let f = NaN; let g = NaN; let h = function() { console.log('h'); } let i = function() { console.log('h'); } let j = 0; let k = -0; let l = Immutable.Map({ a: 1 }); let m = Immutable.Map({ a: 1 }); let n = { a: 1, hashCode: function() { return Immutable.hash('immutable'); }, equals: function() { return true; } }; let o = { a: 1, hashCode: function() { return Immutable.hash('immutable'); }, equals: function() { return true; } }; console.log(Immutable.is(a, b)); // false console.log(Immutable.is(a, c)); // true console.log(Immutable.is(d, e)); // false console.log(Immutable.is(f, g)); // true console.log(Immutable.is(h, i)); // false console.log(Immutable.is(j, k)); // true console.log(Immutable.is(l, m)); // true console.log(Immutable.is(n, o)); // true console.log(Immutable.isValueObject(n)); // true console.log(Immutable.isImmutable(n)); // false console.log(Immutable.isCollection(n)); // false
總結:
1.對於javascript
中原始值的比較相似於 Object.is
須要注意的是:NaN
在Immutable.js
中認爲是與自身相等的;+0和-0在Immutable.js
中認爲相等
2.對於Immutable
中的集合類型,統一做爲值比較。即當兩個集合的值相等的時候即爲相等
3.對於原始值對象,若是提供了hashCode
以及equals
方法,而且返回值相等,也會認爲他們是相等的
主要做用是本身要寫一個Immutable
值對象的時候可能會用到,須要在hashCode
方法中返回一個哈希值。
/** * hash(val) * hash接受一個參數,這個值是任意的,返回一個31位的整數 * 做用:當使用is()函數比較時,經過返回相同的hash值來判斷兩個值是否相等 * 技巧:equals函數返回true, hashCode函數返回相同的hash值來設計兩個值是否相等 */ const Immutable = require('./lib/immutable.js'); let seed1 = 'seed'; let seed2 = { a: 1, b: 2 }; let seed3 = [1, 2, 3, 4]; let seed4 = [1, 2, 3]; let seed5 = Immutable.List([435, 235, 1]); console.log(Immutable.hash(seed1)); // 3526257 console.log(Immutable.hash(seed1)); // 3526257 console.log(Immutable.hash(seed2)); // 1 console.log(Immutable.hash(seed3)); // 2 console.log(Immutable.hash(seed4)); // 3 console.log(Immutable.hash(seed5)); // -53036292
List
繼承自Collection.Indexed
,同時實現了Deque
,可以高效地從首部或者尾部添加或者刪除數據。基本操做與javascript Array相似。更多操做請參看List API
// javascript 數組 const plainArray = [1, 2, 3, 4]; const listFormPlainArray = Immutable.List(plainArray); // iterator const listFromIterator = Immutable.List(plainArray[Symbol.iterator]()); console.log(listFormPlainArray.toJS()); // [1, 2, 3, 4] console.log(listFromIterator.toJS()); // [1, 2, 3, 4]
當index
值爲負數時,表示從尾部進行操做。
const oList = Immutable.List([0, 1, 2]); const addFormLast = oList.set(-1, -1); console.log(addFormLast.toJS()); // [0, 1, -1] const deleteList1 = oList.delete(0); console.log(deleteList1.toJS()); // [1, 2] const deleteList2 = oList.delete(-1); console.log(deleteList2.toJS()); // [0, 1]
List沒有明顯的'unset'(未被設置值)或者'undefined'(值設置爲undefined)數據的概念。在List#forEach中能夠體現。
// unset & undefined const originList = [1, 2, , 4]; const collectionList = Immutable.List(originList); collectionList.forEach(function(v, i) { console.log(`${i} ${v}`); // 0 1 // 1 2 // 2 undefined // 3 4 }); originList.forEach(function(v, i) { console.log(`${i} ${v}`); // 0 1 // 1 2 // 3 4 });
Map
繼承自 Collection.keyed
。Map
是無序的,若是須要有序Map
請使用OrderedMap
。更多操做請參看官網API Map API
Map
的key
是任意的,甚至能夠是NaN
,注意key
值的類型都是string
,能夠看如下例子。
const anyKeyMap = Immutable.Map(); console.log(anyKeyMap.set(key1, 'hello1').get(key1)); // hello1 console.log(anyKeyMap.set(key2, 'hello2').get(key2)); // hello2 console.log(anyKeyMap.set(key3, 'hello3').get(key3)); // hello3 console.log(anyKeyMap.set(key4, 'hello4').get(key4)); // hello4 console.log(anyKeyMap.set(key5, 'hello5').get(key5)); // hello5 // don't initial with a obj like this // { NaN: 'hello' } // Map<V>(obj: {[key: string]: V}): Map<string, V> let key = NaN; const initMap = Immutable.Map({ key: 'hello' }); console.log(initMap.get(key)); // undefined
若是須要在初始化Map
的時候傳入初始值,那麼key
值必須爲string
類型,不然取到的值是undefined
。看下面一個證實key
值都是string
的例子。
let obj = { 1: 'hello' }; console.log(Object.keys(obj)); // ['1'] console.log(obj['1']); // hello console.log(obj[1]); // hello const mapObj = Immutable.Map(obj); console.log(mapObj.get("1")); // hello console.log(mapObj.get(1)); // undefined
下面主要講三個方法:
// update // update([key, newVal,] callback) // 1.傳入key值與回調改變值 // 2.傳入回調函數能夠返回當前值 // 3.傳入key值與新設置的值以及回調函數,注意,若是新值與原來的值不相等,會返回當前值 const originMap = Immutable.Map({ 'key': 'value' }); const newMap1 = originMap.update('key', function(value) { return value + value; }); const newMap2 = originMap.update(function(value) { return value; }); const newMap3 = originMap.update('key1', 'one', function(value) { return value + value; }); const newMap4 = originMap.update('key1', 'one', function(value) { return value; }); console.log(newMap1.toJS()); // { key: 'valuevalue' } console.log(newMap2.toJS()); // { key: 'value' } console.log(newMap3.toJS()); // { key: 'value', key1: 'oneone' } console.log(newMap4.toJS()); // { key: 'value' } // merge // 後面的值覆蓋前面的值 const one = Immutable.Map({ a: 10, b: 20, c: 30 }); const two = Immutable.Map({ a: 40, b: 60, c: 90, d: 100 }); const mergeMap1 = one.merge(two); const mergeMap2 = two.merge(one); console.log(mergeMap1.toJS()); // { a: 40, b: 60, c: 90, d: 100 } console.log(mergeMap2.toJS()); // { a: 10, b: 20, c: 30, d: 100 } // mergeWith const mergeWithMap = one.mergeWith(function(oldVal, newVal) { return oldVal / newVal; }, two); console.log(mergeWithMap.toJS()); // { a: 0.25, b: 0.3333333333333333, c: 0.3333333333333333, d: 100 }
Set
繼承自Collection.Set
,Set
主要的一個特性就是值惟一。所以咱們能夠利用此特性去除重複值。看下面的例子:
const set = Immutable.Set([1, 2, 1, 4]); console.log(set.toJS()); // [1, 2, 4] // 去除list中的相同值 const list = Immutable.List([1, 2, 3, 4, 5, 3, 2, 9, 0]); const setList = Immutable.Set(list); console.log(list); // List [ 1, 2, 3, 4, 5, 3, 2, 9, 0 ] console.log(setList); // Set { 1, 2, 3, 4, 5, 9, 0 }
既然繼承自Collection
,那麼就會存在Collection
中的一些方法。具體操做方法參看官網Set API
// fromKeys const originObj = { a: 1, b: 2, c: 3, d: 4, a: 5 }; const mapIterator = Immutable.Map(originObj)[Symbol.iterator](); const iterator2 = [ ['key', 'value'], ['key1', 'value2'], ['key', 'value3'] ]; console.log(Immutable.Set.fromKeys(mapIterator)); // Set { "a", "b", "c", "d" } console.log(Immutable.Set.fromKeys(iterator2)); // Set { "key", "key1" } console.log(Immutable.Set.fromKeys(originObj)); // Set { "a", "b", "c", "d" } // 交集 // intersect const set1 = Immutable.Set(['a', 'b', 'c']); const set2 = Immutable.Set(['a', 'c', 'd']); const intersected = Immutable.Set.intersect([set1, set2]); console.log(intersected); // Set { "a", "c" } // 並集 // union const unioned = Immutable.Set.union([set1, set2]); console.log(unioned); // Set { "a", "c", "d", "b" } // add const addSet = Immutable.Set([1, 2, 3, 4]); const newSet = addSet.add(5); console.log(newSet.toJS()); // [ 1, 2, 3, 4, 5 ]
Stack
繼承自 Collection.Indexed
。在添加和刪除數據上有很是高的效率。操做老是從棧頂開始,提供的push/pop/peek方法只是由於咱們熟悉了這些API
。不建議使用reverse()
效率不高。具體操做方法參看官網Stack API
const Immutable = require('./lib/immutable.js'); // peek // similar to first const stack = Immutable.Stack([1, 2, 3, 4]); console.log(stack.peek()); // 1 console.log(stack.first()); // 1 // has console.log(stack.has(2)); // true // includes // similar to contains console.log(stack.includes(3)); // true // last console.log(stack.last());
Seq
繼承自 Collection
,Seq
是不可變的,一旦被建立就不可修改,由一些函數引發的變化將會返回一個新的Seq
。Seq
的一個重要特性就是懶計算。只有當被調用時纔會開始計算。具體看如下例子:
const Immutable = require('./lib/immutable.js'); // 在未調用時並不會執行 // 不信能夠將Seq換成List試試,會所有執行 const oddSquares = Immutable.Seq([1, 2, 3, 4, 5, 6, 7, 8]).filter(function(x) { console.log('filter', x); return x % 2 !== 0; }).map(function(x) { console.log('map', x); return x * x; }); // filter 1 // map 1 // 1 console.log(oddSquares.get(0)); // 調用發現,filter中只執行一次,map中也執行了一次
fromJS(val[, callback(key, value, path)])
fromJS
有兩個參數,其中回調函數可選,做用是將原始值類型轉換爲Immutable
的集合。若是不提供回調,默認的轉換行爲是:Array -> Lists, Object -> Maps
const Immutable = require('./lib/immutable.js'); let obj = { a: { b: [10, 20, 30] }, c: 40 }; let iObj = Immutable.fromJS(obj, function(key, value, path) { let isIdxed = Immutable.isIndexed(value); console.log(key, value, path, isIdxed); return isIdxed ? value.toList() : value.toOrderedMap(); }); /** * b Seq [ 10, 20, 30 ] [ 'a', 'b' ] true * a Seq { "b": List [ 10, 20, 30 ] } [ 'a' ] false * b Seq [ 10, 20, 30 ] [ 'a', 'b' ] true * Seq { "a": OrderedMap { "b": List [ 10, 20, 30 ] }, "c": 40 } [] false * b Seq [ 10, 20, 30 ] [ 'a', 'b' ] true * a Seq { "b": List [ 10, 20, 30 ] } [ 'a' ] false * b Seq [ 10, 20, 30 ] [ 'a', 'b' ] true */ console.log(Immutable.isCollection(iObj)); // true console.log(Immutable.isCollection(obj)); // false
Range([start, end, step])
返回一個區間的List
,若step
有值,則在此區間上按照step
來劃分值,默認值:start=1, end=infinity, step=1,if start === end returns []
const Immutable = require('./lib/immutable.js'); console.log(Immutable.Range()); // Range [ 0...Infinity ] console.log(Immutable.Range(10)); // Range [ 10...Infinity ] console.log(Immutable.Range(10, 30, 5)); // Range [ 10...30 by 5 ] console.log(Immutable.Range(10, 10)); // Range [] console.log(Immutable.isImmutable(Immutable.Range())); // true
Record(defaultVal[, description])
Record
必需要有默認值,若是不傳直接報錯,若是傳值爲空對象,後續任何操做將會無效isRecord
方法用來判斷當前對象是不是Record
的一個實例remove
掉的記錄會變爲初始值,以後刪除屢次將會變得無效Record
能夠添加描述Record
能夠被繼承,能夠添加本身的方法賦予更多功能const Immutable = require('./lib/immutable.js'); const DefaultRecord = Immutable.Record({ a: 1, b: 2 }); const RewriteRecord = new DefaultRecord({ b: 3 }); console.log(Immutable.Record.isRecord(DefaultRecord)); // false console.log(Immutable.Record.isRecord(RewriteRecord)); // true const ReRewriteRecord = new DefaultRecord({ b: 4 }); console.log(ReRewriteRecord.get('a')); // 1 console.log(ReRewriteRecord.get('b')); // 4 const removeRecord = ReRewriteRecord.remove('b'); console.log(removeRecord.get('b')); // 2 const reRemoveRecord = removeRecord.remove('b'); console.log(reRemoveRecord.get('b')); // 2 // getDescriptiveName() const Person = Immutable.Record({ name: null }, 'Person'); const me = Person({ name: 'Ryn' }); console.log(me.toString()); // Person { name: "Ryn" } console.log(Immutable.Record.getDescriptiveName(me)); // Person // no-default const NoDefaultRecord = Immutable.Record({}); const writeRecord = new NoDefaultRecord({ a: 1 }); console.log(writeRecord.get('a')); // undefined // extends class ClassRecord extends Immutable.Record({ a: 1, b: 2 }) { getSum() { return this.a + this.b; } } const myClassRecord = new ClassRecord({ b: 3 }); console.log(myClassRecord.getSum());
Repeat(val[, times])
const Immutable = require('./lib/immutable.js'); console.log(Immutable.Repeat('hello')); // Repeat [ hello Infinity times ] console.log(Immutable.Repeat('hello', 4)); // Repeat [ hello 4 times ]
Javascript
中對象都是參考類型,也就是a = { a: 1 }; b = a; b.a = 10;
你發現a.a
也變成10了。可變的好處是節省內存或是利用可變性作一些事情,可是,在複雜的開發中它的反作用遠比好處大的多。因而纔有了淺copy
和深copy
,就是爲了解決這個問題。Immutable.js
的應用主要是在其不變性上,這對於層次比較深的值比較、拷貝上面將會變得十分有用處。