提升你的javascript代碼逼格系列之函數與數組

不知道你們有沒有一種感受,那就是本身寫的javascript代碼雖然能徹底解決工做上的須要,可是,一眼望去,too simple!!!簡直就是一個傻子都能看懂的水平,因而,在工做之餘,我開始去收集一些幾乎在平常開發中不曾用過的javascript寫法/有價值的javascript知識點,藉此來提升自身javascript水平。javascript

1、函數式編程與面向對象編程html

<script>
    /*函數式編程*/
    function f1(){
    var n=999;
    nAdd=function(){
            n+=1;
        };
    var f2 = function(){
      console.log(n);
    };
    return f2;
  }
  var result=f1();
  result(); // 999
  nAdd();
  result(); // 1000

    /*面向對象編程1之json寫法*/
    var obj1 = {
        n:999,
        nAdd:function(){
            this.n += 1;
        },
        getN:function(){
            console.log(this.n);
        }
    };
    obj1.getN();//999
    obj1.nAdd();
    obj1.getN();//1000

    /*面向對象編程2之工廠模式*/
    var obj2 = function(){
        var obj = new Object;
        obj.n = 999;
        obj.nAdd = function(){
            this.n += 1;
        };
        obj.getN = function(){
            console.log(this.n);
        }
        return obj;
    };
    var objins = new obj2();
    objins.getN();//999
    objins.nAdd();
    objins.getN();//1000

    /*面向對象編程3之原型prototype*/
    function obj3(n) {
                this.n = n;
    }
    obj3.prototype.nAdd = function() {
        this.n+=1;
    };
    obj3.prototype.getN = function() {
        console.log(this.n);
    }
    var objins2 = new obj3(999);
    objins2.getN();//999
    objins2.nAdd();
    objins2.getN();//1000
</script>
View Code

針對上面所示的函數式編程,涉及了做用域和閉包的知識,這裏簡要提一下;java

裏面的nAdd沒有用var關鍵字聲明,因此他是全局的,後面能夠全局調用,其次利用閉包訪問和修改了函數做用域內的n值。es6

2、純函數和非純函數編程

1.此函數在相同的輸入值時,老是產生相同的輸出。函數的輸出和當前運行環境的上下文狀態無關。json

2.此函數運行過程不影響運行環境,也就是無反作用(如觸發事件、發起http請求、打印/log等)。數組

簡單來講,也就是當一個函數的輸出不受外部環境影響,同時也不影響外部環境時,該函數就是純函數,也就是它只關注邏輯運算和數學運算,同一個輸入總獲得同一個輸出。緩存

var xs = [1,2,3,4,5];
// 純的
xs.slice(0,3);
//=> [1,2,3]
xs.slice(0,3);
//=> [1,2,3]
xs.slice(0,3);
//=> [1,2,3]

// 不純的
xs.splice(0,3);
//=> [1,2,3]
xs.splice(0,3);
//=> [4,5]
xs.splice(0,3);
//=> []
View Code

純函數相對於非純函數來講,在可緩存性、可移植性、可測試性以及並行計算方面都有着巨大的優點。閉包

3、函數的柯里化ide

curry 的概念很簡單:將一個低階函數轉換爲高階函數的過程就叫柯里化。

函數柯里化是一種「預加載」函數的能力,經過傳遞一到兩個參數調用函數,就能獲得一個記住了這些參數的新函數。從某種意義上來說,這是一種對參數的緩存,是一種很是高效的編寫函數的方法:

                /*通常寫法:兩數相加*/
        var add = function(x,y){
            return x + y;
        }
        /*2+1和2+10*/
        add(2,1);//3
        add(2,10);//12

        /*柯里化寫法*/
        //es5
        var add = function(x) {
          return function(y) {
            return x + y;
          };
        };
        //es6
        var add = x => (y => x + y);

        //定義+1和+10函數
        var increment = add(1);//柯里化後獲得的+1函數
        var addTen = add(10);//柯里化後獲得的+10函數
        increment(2);  // 3
        addTen(2);  // 12
View Code

4、Array.reduce/Array.reduceRight(數組迭代)

先不看標題,來個題目:如何求數組[1,2,3,4,5]的各項累加的和?

大多數人恐怕會當即說出for循環、while循環,的確,for循環和while循環是能夠解決上述問題,可是,這個答案顯然脫離了此篇博客的主題:逼格!!!

而且,實驗代表,將上述求和方法循環1W次,用for循環和while循環耗時大概在50~55ms,可是,用Array.reduce/Array.reduceRight僅僅耗時0.4ms!!!

關於該方法的詳解,請猛戳→https://www.w3cplus.com/javascript/array-part-8.html;這裏給個demo:

var arr = [1,2,3,4,5];
        /*ES6寫法*/
        var reducer = (prevalue,curvalue)=>prevalue + curvalue;
        console.log(arr.reduce(reducer));//15
        /*ES5寫法*/
        var reducer = function(prevalue,curvalue){
            return prevalue + curvalue;
        };
        console.log(arr.reduce(reducer));//15
        /*解析*/
        數組的reduce方法有兩個參數:callback,prevalue;
        對於第一個參數callback,裏面有4個參數prevalue、curvalue、index、array;
        index是當前迭代時的curvalue的索引(數組中的索引),array固然是操做的數組了;
        着重看另外兩個參數,prevalue和curvalue(上一個值和當前值);
        若是reduce方法沒有第二個參數的話,那麼callback回調將數組第一個值做爲prevalue,第二個值做爲curvalue;
        若是reduce方法有第二個參數,那麼將第二個參數值做爲prevalue,將數組第一個值做爲curvalue;
        每一次的操做結果將變爲下一次操做的prevalue,而curvalue的值教上一次日後推一位;
        /*實例解析*/
        針對上述數組相加;
        沒有第二個參數,只有一個回調callback,那麼他的過程是這樣的;
        第一次運算:prevalue爲1,curvalue爲2,運算結果爲1+2=3,並將結果做爲下次運算的prevalue;
        第二次運算:prevalue爲3(上一次運算的結果),curvalue爲3,運算結果爲3+3=4,以此類推;

        若是有第二個參數,假設爲5,arr.reduce(reducer,5),那麼將是這麼計算的;
        第一次運算:prevalue爲5(第二個參數),curvalue爲1,運算結果爲5+1=6,並將結果做爲下次運算的prevalue;
        第二次運算:prevalue爲6(上一次運算的結果),curvalue爲2,運算結果爲6+2=8,以此類推;

        reduceRight和reduce相似,只不過他是從右邊開始迭代的。
View Code

 5、函數組合(reduce高級用法)

假想一下,要將字符串「my name is echo」進行以下操做,你會怎麼作?轉大寫、而後末尾加一個感嘆號;

我想你大概會定義一個函數operate,對於形參str,先toUpperCase(),而後+ '!',ok這樣作沒錯,可是如今我需求改了,我不要求轉大寫了,你是否是得刪除那行代碼?

可能你會說了,我定義兩個函數一個是轉大寫,一個是加歎號,須要哪一個用哪一個,其實不管你用幾個函數來寫,仍是直接用prototype或者鏈式調用的方法去寫,都只是大衆化的寫法,且不易維護,so,請看下面的寫法:

        var compose = (...args) => x => args.reduceRight((prevalue, curvalue) => curvalue(prevalue), x);
        var toUpperCase = x => x.toUpperCase();
        var exclaim = x => x + '!';
        var shout = compose(exclaim, toUpperCase);
        console.log(shout("my name is echo"));
View Code

 一眼望去,是否是兩眼懵逼?!!不怕,咱們用ES5還原一下,請看:

        var compose = function (...args){
            return function (x){
                return args.reduceRight(function(prevalue, curvalue){
                    return curvalue(prevalue);
                },x)
            }
        };
        var toUpperCase = function(x){
            return x.toUpperCase();
        };
        var exclaim = function(x){
            return x + '!';
        };
        var shout = compose(exclaim, toUpperCase);
        console.log(shout("my name is echo"));
View Code

結合上述第四例所講到的reduce,我但願此時你已經看懂了,沒看懂也不要緊,接着往下看,繼續解析:

        var shout == compose(exclaim, toUpperCase)
                  == function (x) {
                      return [exclaim, toUpperCase].reduceRight(function(prevalue, curvalue){
                        return curvalue(prevalue);
                    },x)
                  };
        shout("my name is echo") == [exclaim, toUpperCase].reduceRight(function(prevalue, curvalue){
                        return curvalue(prevalue);
                    },"my name is echo");
        /*前面說過,若是reduce/reduceRight傳了第二個參數,那麼該第二個參數將做爲prevalue給callback調用。*/
        /*運算過程以下(reduceRight從右往左)curvalue(prevalue):*/
        1.toUpperCase("my name is echo");並將結果做爲prevalue供下次調用;
        2.exclaim(toUpperCase("my name is echo"));如此實現了先轉大寫再加感嘆號。
View Code
相關文章
相關標籤/搜索