Function Programming
本篇文章是自己對於JavaScript函數語言程式設計一書的讀書筆記。利用underscore框架介紹函數語言程式設計幾個概念。
高階函式
1. 將函式作為引數;
複製程式碼
<!--repeatedly值版本-->
function repeatedly(times,value){
return _.map(_.range(times),()=>{ return value })
}
<!--repeatedly 函式版本-->
function repeatedly(times,fun){
return _.map(_.range(times),fun)
}
repeatedly(3,function(){
return Math.floor(Math.random()*10+1)
})
複製程式碼
repeatedly是函數語言程式設計的一個典型思維,將值變成函式。
2. 返回其他函式的函式。
複製程式碼
react和redux裡面用了大量的返回其他函式的函式。包括高階元件,applyMiddleware函式。 Thunk函式也是一個返回其他函式的函式。
var Thunk = function(fn){
return function(){
var args = Array.prototype.slice.call(arguments);
return function(callback){
args.push(callback);
return fn.apply.(this,args);
}
}
}
var readFileThunk = Thunk(fs.readFile);
readFileThunk(fileA)(callback);
複製程式碼
由函式構建函式
- 函式式的精華(dispatch函式);
function existy(x) {
return x != null;
}
function doWhen(cond, action) {
if (tructhy(cond))
return action();
else
return undefined;
}
function invoker(NAME, METHOD) {
return function (target) {
if (!existy(target)) {
alert('Must provide a target');
}
var targetMethod = target[NAME];
var args = _.rest(arguments);
return doWhen(existy(targetMethod) && METHOD === targetMethod, function () {
return targetMethod.apply(target, args);
});
}
}
function dispatch() {
var funs = _.toArray(arguments);
var size = funs.length;
return function (target) {
var ret = undefined;
var args = _.rest(arguments);
for (var funIndex = 0; funIndex < size; funIndex++) {
var fun = funs[funIndex];
ret = fun.apply(fun,construct(target,args))
if(existy(ret)) return ret;
}
if(existy(ret)) return ret;
}
}
var str = dispatch(invoker('toString',Array.prototype.toString),invoker('toString',String.prototype.toString));
console.log(str('a'));
console.log(str(_.range(10)));
複製程式碼
dispatch將多個invoker組合在一起,形成多型函式,或根據不同的引數產生不同行為的引數。 2. 柯里化。 對於每個邏輯引數,柯里化函式會逐漸返回已配置好的函式,直到所有的引數用完。
function curry(fun){
return function(firstArg){
retun function(secondArg){
return fun(firstArg,secondArg);
}
}
}
複製程式碼
- 部分應用。 柯里化函式逐漸返回消耗引數的函式,直到所有的引數耗盡,然而部分應用函式是一個"部分執行",等待接收剩餘的引數立即執行的引數。
function partApply(f, x) {
return function(y) {
return f(x, y);
}
}
複製程式碼
- 通過組合端至端的拼接函式。 一個理想化的函式式程式是向函式流水線的一端輸送的一塊資料,從另一端輸出一個全新的資料塊。 例如: !_isString(name);
- _isString接收一個物件,並返回一個布林值。
- !接收一個布林值,並返回一個布林值。
_.compose函式從右往左執行。也就是說,最右邊的函式的結果會被送入其左側的函式,一個接一個。
function not(x){
return !x;
}
var isntString = _.compose(not,_isString);
複製程式碼
純度,不變性和更改政策。
- pure function
- 其結果只能從它的引數的值來計算。
- 不能依賴於能被外部操作改變的資料。
- 不能改變外部狀態。 javaScript會導致不純的函式或方法如Date.now(),console.log(),this和全域性變數。
- 資料的不可變性。(immutablejs);
function touchAndLog(touchFn) {
let data = { key: 'value' };
touchFn(data);
console.log(data.key); // 猜猜會列印什麼?
}
複製程式碼
在不檢視 touchFn 的程式碼的情況下,因為不確定它對 data 做了什麼,你是不可能知道會列印什麼(這不是廢話嗎)。但如果 data 是 Immutable 的呢,你可以很肯定的知道列印的是 value。
- 更改政策。 隨便拿到一個物件並改變它,更好的策略是把物件放入容器中,並更改容器。
基於流的程式設計。
- 惰性鏈。
function LazyChain(obj){
this._calls = [];
this._target = obj;
}
LazyChain.prototype.invoke = function(methodName){
var args = _.rest(arguments);
this._calls.push(function(target){
var meth = target[methodName];
return meth.apply(target,args);
})
return this;
}
LazyChain.prototype.force = ()=>{
return _.reduce(this._calls,function(target,thunk){
return thunk(target);
},this._target)
}
new LazyChain([1,2,3]).invoke('sort').force();
複製程式碼