深入了解Javascript函數式編程

HeleneAEG 8年前發布 | 17K 次閱讀 函數式編程 JavaScript開發 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

來自: http://div.io/topic/1678

查看源碼: https://github.com/AutumnsWind/Good-text-Share/issues/13

 本文由用戶 HeleneAEG 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!