JavaScript 函數式編程讀書筆記2
概述
這是我讀《javascript函數式編程》的讀書筆記,供以後開發時參考,相信對其他人也有用。
說明:雖然本書是基於underscore.js庫寫的,但是其中的理念和思考方式都講的很好,值得一讀。不過如果不熟悉underscore.js庫的話,讀起來會有點困難。
《javascript函數式編程》讀書筆記1
對象的不變性
函數式編程中函數是一等公民,所以對於數據來說,需要他們是不可變的。另外,對象的不可變確實能夠帶來一些好處,比如:如果數據是不可變的,那麽可以直接通過“===”來判斷數據是否發生變化,這使得react的效率得到極大提升。下面我們來討論在js中實現immutable對象的方法。
freeze
用Object.freeze可以凍結一個數組或者對象,當使用Object.freeze的時候,將導致後續的變化失敗。如果在嚴格模式下使用,將拋出一個TypeError;否則,所有的變化將悄悄地失敗。實例如下:
var a = [2,3,4];
a[1]; //輸出3
Object.freeze(a);
a[1]=5;
a[1]; //輸出3
也有一個isFrozen API來判斷a是否確實被凍結。
Object.isFrozen(a); //輸出true
但是這麽實現有如下幾個痛點:
- 變化會悄悄地失敗,可能會導致微妙的錯誤。
- 不是所有的庫都支持freeze。
- 這個是最重要的,Object.freeze是一個淺操作
由於Object.freeze是一個淺操作,所以嵌套對象裏面的變量不會被凍結,除非用遞歸進行深凍結,代碼如下:
function isObject(obj) { var type = typeof obj; return type === 'function' || type === 'object' && !!obj; } function deepFreeze(obj) { if(!Object.isFrozen(obj)) Object.freeze(obj); for (var key in obj) { if(!obj.hasOwnProperty(key) || !isObject(obj)) continue; deepFreeze(obj[key]; } }
使用容器
我們也可以使用深復制,並把它放在一個容器裏面來保持變量的不變性。簡單代碼如下:
function isObject(obj) {
var type = typeof obj;
return type === 'function' || type === 'object' && !!obj;
}
function deepClone(obj) {
if(!isObject(obj)) return obj;
var temp = new obj.constructor();
for (var key in obj) {
if(obj.hasOwnProperty(key))
temp[key] = deepClone(obj[key]);
}
return temp;
}
function Container(init) {
this.state = init;
}
Container.prototype = {
setState: function(obj) {
this.state = deepClone(obj);
return this.state;
}
}
var aObj = new Container({a:{b:1}, c:2});
aObj.setState({a:{b:3}, c:6, d:8});
aObj; //輸出{a:{b:3}, c:6, d:8}
使用immutable.js
使用上面的方法實現不可變數據問題很多,用freeze會悄悄地失敗,並且是淺操作,使用容器的話深復制對象會消耗大量的內存,並且速度很慢。
immutable.js就是facebook開源的為了解決這個問題的庫。它可以說是和react一樣的劃時代的產物。它通過參考hash maps tries和hash maps tries提供了一種優雅地實現javascript Immutable Data的方式。
它使用了Structural Sharing(結構共享),即如果對象樹中一個節點發生變化,只修改這個節點和受它影響的父節點,其它節點則進行共享。所以避免了深拷貝帶來的CPU 和內存的浪費。
具體可參考:
搞定immutable.js詳細說明
IMMUTABLE 詳解
JavaScript 函數式編程讀書筆記2