2016年6月17日 星期五

[筆記] 了解JavaScript中functional programming的概念


在我們認識了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]
→回到此系列文章目錄

Share:

0 意見:

張貼留言