在我們認識了First Class Function還有function當中內建的一些方法後,這篇文章談談JavaScript強大之處─Functional Programming。
讓我們開始吧
首先,讓我們一樣從程式碼看起,假設我們現在有一個陣列叫做arr1,而我想要建立另一個陣列arr2,而裡面的元素都是arr1 x 2,在一般的方式下我們通常會這樣寫:
var arr1 = [1,2,3];
console.log(arr1);
var arr2 = [];
for (var i = 0; i < arr1.length ; i++){
arr2.push(arr1[i]*2);
}
console.log(arr2);
這時候我們就可以得到arr2 = [2, 4, 6] 的結果。但是,身為一個工程師,我們總是想要透過最少的程式碼來達到同樣的效果,而且要避免類似的程式碼重覆,為了達到這樣的目的,我們要來試試看用Functional Programming的概念來改寫這段程式。
首先,我們要把上面寫的程式碼"函式化",第一步就是要把可能會變動的值當成function當中的參數來處理,所以我們的function會包含兩個參數,一個是代入的陣列,稱做arr,一個是要對這個陣列執行的內容,稱做fn:
function mapForEach(arr, fn) {
var newArr = [];
for (var i = 0; i < arr.length; i++) {
newArr.push(
fn(arr[i])
);
};
return newArr;
}
接著,如果我們同樣要根據所傳入的陣列,將所有的元素值都乘上2的話,就可以在第一個參數arr的地方代入我們要被處理的陣列,第二個參數fn的地方,直接代入一個function expreesions來表達我們要對arr1所處理的內容:
var arr2 = mapForEach(arr1, function(item) {
return item * 2;
})
console.log(arr2);
如此,arr2就會一樣得到[2, 4, 6]的結果。透過functional programming可以延伸出多種變化
透過這樣functional programming的方式,我們可以很容易的,就對我們想要針對array做不同的處理,例如說,我現在想要判斷在arr1這個陣列中,有哪些元素值是大於2的,我們就可以這樣做:
var arr3 = mapForEach(arr1, function(item) {
return item > 2;
});
console.log(arr3); // false, false, true
如此就可以透過同一個mapForEach這個function針對不同的陣列做出不同的效果functional programming再進化
我們剛剛有提到,我們可以把我們所做的事情透過function的方式來處理,現在,我們你會發現在原本mapForEach的參數當中,我們代了一個function expression,而這個function reuturn的內容則會根據函式中不同的數值而定。這代表我們也可以把這個數值,變成參數之一,我們可以另外建立一個新的function,名為checkLimiter:
function mapForEach(arr, fn) {
var newArr = [];
for (var i = 0; i < arr.length; i++) {
newArr.push(
fn(arr[i])
);
};
return newArr;
}
var checkLimiter = function(limiter, item) {
return item > limiter;
}
var arr4 = mapForEach(arr1, checkLimiter(2, ...));
但這樣寫會碰到一個問題,就是在我們原本第8行的地方,會發現fn這個參數只能帶入一個變數,可是我們後來寫的checkLimiter在第20行的地方卻需要代入兩個參數才能,因此,這時候我們需要另一個解決的辦法!這個辦法就是我們在上一篇所使用到的.bind( ),透過bind,我們可以讓函數當中的參數變成預設值,因此等於只需要填入另一個參數就可以了!所以,我們可以把第20行變成:
var arr4 = mapForEach(arr1,checkLimiter.bind(this,2));
console.log(arr4) // [false, false, true];
就可以正確執行了將checkLimiter簡化為只代入Limiter這個參數
但現在checkLimiter這個函式中,我們需要代入兩個參數,分別是limiter和item,那我們有沒有辦法只代入Limiter這個參數,所以不用每次都用.bind來達到一樣的效果呢?
有兩種方式都可以達到這個目的,簡單來說,我們都是要創造一個新的function來return我們剛剛所撰寫的那個function,我們把這個新的函式稱做checkLimiterSimplified,第一種寫法是這樣:
var checkLimiterSimplified = function(limiter) {
return function(limiter, item) {
return item > limiter;
}.bind(this, limiter);
}
var arr5 = mapForEach(arr1, checkLimiterSimplified(2));
console.log(arr5); // [false, false, true]
另一種則是像這樣
var checkLimiterSimplified = function(limiter) {
return function(item) {
return item > limiter;
}
}
var arr6 = mapForEach(arr1, checkLimiterSimplified(2));
console.log(arr6); // [false, false, true]
這兩種寫法都能達到一樣的效果。
程式範例
var arr1 = [1, 2, 3]; console.log(arr1); /* var arr2 = []; for (var i = 0; i < arr1.length ; i++){ arr2.push(arr1[i]*2); } console.log(arr2); */ function mapForEach(arr, fn) { var newArr = []; for (var i = 0; i < arr.length; i++) { newArr.push( fn(arr[i]) ); }; return newArr; } /*------------------------------------------*/ var arr2 = mapForEach(arr1, function(item) { return item * 2; }) console.log(arr2); // 2, 4, 6 /*------------------------------------------*/ var arr3 = mapForEach(arr1, function(item) { return item > 2; }); console.log(arr3); // false, false, true /*------------------------------------------*/ var checkLimiter = function(limiter, item) { return item > limiter; } var arr4 = mapForEach(arr1, checkLimiter.bind(this, 2)); console.log(arr4); // false, false, true /*------------------------------------------*/ var checkLimiterSimplified = function(limiter) { return function(limiter, item) { return item > limiter; }.bind(this, limiter); } var arr5 = mapForEach(arr1, checkLimiterSimplified(2)); //console.log(arr5); // [false, false, true] /*------------------------------------------*/ var checkLimiterSimplified = function(limiter) { return function(item) { return item > limiter; } } var arr6 = mapForEach(arr1, checkLimiterSimplified(2)); console.log(arr6); // [false, false, true]→回到此系列文章目錄
0 意見:
張貼留言