為什麼用Immutable.js代替普通js物件?
將資料視為不可變,將給你帶來很多好處。事實上,這是也React背後的原理:React的元素是不可變的。
但是用Immutable.js有什麼好處呢?
首先有一個非常巨大的物件…
這裡有100,000條待辦事項:
var todos = {
⋮
t79444dae: { title: `Task 50001`, completed: false },
t7eaf70c3: { title: `Task 50002`, completed: false },
t2fd2ffa0: { title: `Task 50003`, completed: false },
t6321775c: { title: `Task 50004`, completed: false },
t2148bf88: { title: `Task 50005`, completed: false },
t9e37b9b6: { title: `Task 50006`, completed: false },
tb5b1b6ae: { title: `Task 50007`, completed: false },
tfe88b26d: { title: `Task 50008`, completed: false },
⋮
(100,000 items)
}複製程式碼
我要把第50,005條任務的completed改為ture。
用普通的JavaScript物件
function toggleTodo (todos, id) {
return Object.assign({ }, todos, {
[id]: Object.assign({ }, todos[id], {
completed: !todos[id].completed
})
})
}
var nextState = toggleTodo(todos, `t2148bf88`)複製程式碼
這項操作執行了134ms。
為什麼用了這麼長時間呢?因為當使用Object.assign
,JavaScript會從舊物件(淺)複製每個屬性到新的物件。
我們有100,000條待辦事項,所以意味著有100,000個屬性需要被(淺)複製。
這就是為什麼花了這麼長時間的原因。
在JavaScript中,物件預設是可變的。
當你複製一個物件時,JavaScript不得不復制每一個屬性來保證這兩個物件相互獨立。
100,000個屬性被(淺)複製到新的物件。
使用Immutable.js
var todos = Immutable.fromJS({
⋮
t79444dae: { title: `Task 50001`, completed: false },
t7eaf70c3: { title: `Task 50002`, completed: false },
t2fd2ffa0: { title: `Task 50003`, completed: false },
t6321775c: { title: `Task 50004`, completed: false },
t2148bf88: { title: `Task 50005`, completed: false },
t9e37b9b6: { title: `Task 50006`, completed: false },
tb5b1b6ae: { title: `Task 50007`, completed: false },
tfe88b26d: { title: `Task 50008`, completed: false },
⋮
(100,000 items)
})
// 使用[updeep](https://github.com/substantial/updeep)
function toggleTodo (todos, id) {
return u({
[id]: {
completed: (completed) => !completed
}
}, todos)
}
var nextState = toggleTodo(todos, `t2148bf88`)複製程式碼
這項操作執行了1.2ms。速度提升了100倍。
為什麼會這麼快呢?
可持久化的資料結構
可持久化的資料結構強制約束所有的操作將返回新版本資料結構並且保持原有的資料結構不變,而不是直接修改原來的資料結構。
這意味著所有的可持久化資料結構是不可變的。
鑑於這個約束,第三方庫在應用可持久化資料結構後可以更好的優化效能。
效能提升
為了直觀的觀察,讓我們嘗試一個小小例子。
鍵值對對映資料:
我們可以用一個普通的JavaScript物件來儲存:
const data = {
to: 7,
tea: 3,
ted: 4,
ten: 12,
A: 15,
i: 11,
in: 5,
inn: 9
}複製程式碼
轉換成一棵資料樹,像這樣:
圖片來源: 維基百科
通常來講,你可以從根節點沿著上圖的路徑來獲取你想要的值。
如果你想獲取data.in
,從根節點開始,沿著i,n,就可以到達值為5的節點。
如何去修改呢?
比如我們想把鍵為tea的值3改成14。
我要建造一個新的樹,並且儘可能重用已經存在的節點
綠色表示新建的節點。
灰色的節點失去的索引,將會被回收
正如你所看到的,我們只是重新建立了4個新的節點來更新這個樹。其他節點都被重用了。
這叫 結構共享
Immutable.js就是通過這種方式實現Immutable.Map
。建立一個樹,每個節點最多有32個分支。
這張圖展示了一個真實的Immutable.Map
當我們更新一個節點,只有幾個節點需要被重新建立。
Immutable.js通過建立多種型別的節點來保持樹結構的緊湊和效能:
萬物皆有兩面性…
請不要認為這篇文章的意思是“你應該經常使用Immutable.js。”,準確的講,我只是告訴你用它的所有好處,以及為什麼要使用它。
當我在寫程式碼的時候,我首先會用普通的JavaScript物件和陣列,當我使用Immutable.js時,我需要非常確定,比如一個物件包含10,000個屬性。我幾乎從不使用Immutable.js,因為大多數時候的物件都很小。