深入了解Javascript函數式編程
初階部分
字符串可以保存為變量,函數說他也可以
var autumn = 'autumnswind';
var autumn_fn = function() {
return 'autumnswind';
};
字符串可以保存對象字段,函數說他也可以
var autumnswind = {
autumn: 'autumnswind',
autumn_fn: function() {
return 'autumnswind'
}
};
字符串可以用時再創建,函數說他也可以
'Autumns' + (function() {
return 'Wind'
})();
字符串可以作為參數傳給函數,函數說他也可以
var autumn;
function autumn_fn(){};
function hellloWorld(autumn, autumn_fn) {
return;
}
字符串可以作為函數返回值,函數說他也可以
return 'autumnswind';
return function() {
return 'autumnswind';
};
所以函數真的是JS里面的上等好公民啊~可以穿梭于任何地方,并談笑風生~ 高階部分:
有了上面的例子,那么我們就可以把函數這個小孩子帶到好多好玩的地方,往下慢慢看~
首先看第一個例子
var
obj1 = {
value: '秋風'
},
obj2 = {
value: 'autumnswind'
},
obj3 = {
value: '莎娜'
};
var values = [];
function add(obj) {
values.push(obj.value);
}
add(obj1);
add(obj2);
console.log(values); // 秋風,autumnswind
</code></pre>
這種寫法,大家都知道了,變量會污染全局環境,并且一旦有一個地方忘記修改了就會變得很不穩定,不像函數體一樣,只負責內部的輸入輸出,這里如果融入上下文的其他函數和變量就會變得非常混亂
根據這樣修改成第二個例子:
var
obj1 = {
value: '秋風'
},
obj2 = {
value: 'autumnswind'
},
obj3 = {
value: '莎娜'
};
function add(obj) {
var values = [];
values.push(obj.value);
return values;
}
跟下面一樣
/*function add(obj) {
var values = [];
var accumulate = function() {
values.push(obj.value);
};
accumulate();
return values;
}*/
add(obj1);
add(obj2);
console.log(add(obj1)); // 秋風
console.log(add(obj2)); // autumnswind
</code></pre>
這樣我們把values數組聲明放進去函數內部,這樣就不會被其他外部變量騷擾了,但是問題又來了,只有最后傳入對象值才可以返回,那不是我們的初衷
最后看消除所有尷尬的第三個例子:
var
obj1 = {
value: '秋風'
},
obj2 = {
value: 'autumnswind'
},
obj3 = {
value: '莎娜'
};
var
Add = function(obj) {
var
values = [];
var
_calc = function(obj) {
if (obj) {
values.push(obj.value);
return values;
} else {
return values;
}
};
return _calc;
//可以這樣把它看成匿名函數省去一個沒必要的變量
/*return function(obj) {
if (obj) {
values.push(obj.value);
return values;
} else {
return values;
}
}*/
};
var
calc = Add();
calc(obj1); //相當于ValueAccumulator()(obj1)
calc(obj2); //走if分支
console.log(calc()); //走else的分支
這里制造一個閉包來保存第一層函數的values數組,這個數組既可以被內部函數 calc調用,也可以在自己函數體內調用;第一層函數的關鍵點在于返回的是在自己內部定義的那個函數 calc,這個hack就能讓Add函數在外部能訪問函數體內的一直保存的values數組,進一步說這種方法可以得到第一層函數任何一個聲明的變量,這是閉包的一個很常見的用法(返回函數)。
理解下面這些話
JS中的閉包就是函數可以訪問父作用域(這個總結夠短的,有爭議的可以先擱置爭議)
上面其實可以看做是自執行的匿名函數(_calc可以它看成一個匿名函數輸出來),自執行的函數實際上是高階函數的一種形式。高階函數就是以其它函數為輸入,或者返回一個函數為輸出的函數(這個理解越來越像是閉包的理解了)
所以函數將其他函數作為輸入參數或者作為返回值,就可以稱為高階函數
上面其實還需要了解一個知識點就是純函數
//非純函數
var autumn1 = function(str) {
window.innerHeight;
window.innerWidth;
return str + 's innerHeight: ' + window.innerWidth + 'px';
};
//純函數
var autumn2 = function(str, height) {
return str + 's innerHeight: ' + height + 'px';
};
console.log(autumn1('autumnswind'));
console.log(autumn2('autumnswind', 254));
兩個函數的區別在于非純函數依賴window這個全局對象的狀態來計算寬度和高度,而自給自足的純函數則要求這些值作為參數傳入,當一個函數是純的,也就是不依賴于當前狀態和環境,我們就不用管它實際的計算結果是什么時候被計算出來。
純函數返回的計算結果僅與傳入的參數相關
純函數是完完全全獨立的,所以它適合被重復調用使用,為下面的函數編程做一個好鋪墊
有更深刻理解或者有誤的話請務必提醒并留言我~
回到題目核心的JS函數編程
由于JS中,函數是頭等公民,這里的實際意思就是函數被認定最基本最根本的類型,正如數字和對象一樣。 如果數字和對象可以被來回傳遞,那么函數(函數我為什么就不可以呢)也可以的。
這就是JS函數編程的基本思想吧
那下面第四段代碼就是在第三段代碼后面的基礎上再添加,實現這種思路
var calc2 = Add();
var objects = [obj1, obj2, obj3]; // 這個數組可以很大
objects.forEach(calc2);
//注意對象foeEach的用法 array.forEach(callbackfn[, thisArg]);對于數組中的每個元素,forEach都會調用callbackfn函數一次
console.log(calc2());
這里的calc2函數就變成一個‘變量’進入到forEach參數內
我們可以在這個基礎上繼續擴充功能,第五段代碼,鏈式調用
objects1 = objects.reverse();
objects2 = objects1.concat([4, 16]);
objects3 = objects2.map(Math.sqrt);
console.log(objects3);
用jQuery用多了,就會知道,jq習慣把類似上面的代碼鏈式調用類似這樣把代碼串在一起
console.log(['秋風', 'autumnswind', '莎娜'].reverse().concat([4, 16]).map(Math.sqrt)); //NaN,NaN,NaN,2,4
//寫明顯一點
/*console.log(
['秋風', 'autumnswind', '莎娜']
.reverse()
.concat([4, 16])
.map(Math.sqrt)
);*/
這樣用的前提必須是這個函數對象里面擁有這個方法,換句話說就是Array.prototype有這個方法(例如reverse, concat ,map)給他擴充一個簡單的方法如下:
Array.prototype.make4 = function() {
console.log(this[4]);
//this.length=4;
return this[4];
}
console.log(['秋風', 'autumnswind', '莎娜'].reverse().concat([4, 16]).map(Math.sqrt).make4()); //4
上面除了展示匿名函數,鏈式調用,還有有一種方法是函數式編程里面常用的,就是遞歸
var Add2 = function(n) {
if (n < 0) {
// 基準情形
return 'I am autumnswind';
} else {
// 遞歸情形
return Add2(n - 1);
}
}
console.log(Add2(5));
注意,基準情形就是讓遞歸停止下來的條件,防止無限遞歸
再復雜點就叫分而治之,其實數學界很多問題都可以用遞歸解決的
var Add3 = function(a, b) {
if (a < 0 && b == 8) {
console.log(a % b); //-1
// 基準情形
return 'I am autumnswind';
} else {
// 遞歸情形
return Add3(a - 1, b);
}
}
console.log(Add3(5, 8));
console.log(Add3(5, 7)); //Maximum call stack size exceeded
查看源碼: https://github.com/AutumnsWind/Good-text-Share/issues/13