高階函數

本文原創:yuanlan前端

定義

  • 接收函數做爲參數
  • 返回值是函數
函數做爲參數傳遞
  • Array.prototype.map,
  • Array.prototype.filter
  • Array.prototype.reduce
  • ajax回調
  • ....
函數做爲返回值
var isType = function(type){
    return function(obj){
        return Object.prototype.toString.call(obj) === '[object '+type+']';
    }
}
複製代碼

單例模式:ajax

var getSingle = function(fn){
    var res;
    return function(){
        return res || (res = fn.apply(this,arguments));
    }
}

var getStr = getSingle(function(){
    return new String('123')
});

var str1 = getStr();
var str2 = getStr();

console.log(str1===str2); //true
複製代碼

應用

面向切面編程
  • 讓一個函數在另外一個函數的以前或者以後執行
  • 抽出來非業務的代碼,保持業務代碼的整潔
  • 無侵入式編程,就是在不修改原有代碼的基礎上,對原始代碼進行一些功能拓展。常能夠應用於諸如統計、日誌打印、原始函數功能補充等場景中。
Function.prototype.before = function(brforeFn){
    var self = this;
    return function(){
        brforeFn.apply(this, arguments);
        return self.apply(this,arguments);
    }
}

Function.prototype.after = function(afterFn){
    var self = this;
    return function(){
        var ret = self.apply(this, arguments);
        afterFn.apply(this, arguments);
        return ret;
    }
}
複製代碼

主要應用:編程

1.防止window.onload被二次覆蓋瀏覽器

window.onload = function(){
    console.log(1);
    console.log(2);
}
複製代碼

直接修改原始的代碼,徹底侵入式編程bash

改進:markdown

var _onload = window.onload;
window.onload = function(){
    if(_onload){
        _onload();
    }
    console.log(2);
}
複製代碼

多出來一個變量,增長了額外的成本閉包

再次改進:app

window.onload = (window.onload || function(){}).after(function(){
    console.log(2);
})
複製代碼

2.統計日誌上報dom

例如統計一下1000個dom插入body所花的時間函數

var createDom = function(){
    var start = new Date().getTime(); // 屬於時間統計,與業務沒有關係
    for(var i=0;i<1000;i++){
        var oDiv = document.createElement('div');
        oDiv.innerHTML = i;
        document.body.appendChild(oDiv);
    }
    var time = new Date().getTime() - start;// 屬於時間統計,與業務沒有關係
    console.log('createDom:' + time);// 屬於時間統計,與業務沒有關係
}
複製代碼

能夠刪除這些統計的代碼,定義一個通用的包裝器

var logTime = function(fn,fn_name){
    return fn = (function(){
        var start;
        return fn.before(function(){
            start = new Date().getTime();
        }).after(function(){
            var time = new Date().getTime() - start;
            console.log(fn_name+':'+time);
        })
    })();
}
createDom = logTime(createDom,'createDom');
createDom();

複製代碼
currying

又稱部分求值 首先會接收一些參數。接收了參數以後並不會當即求值,而是繼續返回另一個函數,剛纔傳入的參數在函數造成的閉包中被保存起來。待到函數真正求值的時候,以前傳入的參數都會被一次性用於求值。

var currying = function(fn){
    var args = [];
    var f = function(){
        if(arguments.length === 0){
            return fn.apply(this, args)
        }else{
            [].push.apply(args,arguments); 
            return f;
        }
    }
    return f;
}  

var costFn = (function(){
    var res = 0;
    return function(){
        for(var i=0,len = arguments.length;i<len;i++){
            res+=arguments[i];
        }
        return res;     
    }

})();

var costCur = currying(costFn);
console.log(costCur(100)); //[Function: f]
console.log(costCur(200)); //[Function: f]
console.log(costCur(300)); //[Function: f]
console.log(costCur()); //600

複製代碼
uncurrying

借用其餘對象的方法

Function.prototype.uncurrying = function(){
    var self = this; //self : Array.prototype.push
    return function(){
        var obj = Array.prototype.shift.call(arguments); //obj: {'length':0,'0':1}
        return self.apply(obj,arguments);//Array.prototype.push.apply(obj,[2]);
    }
}

複製代碼
Function.prototype.uncurrying = function(){
    var self = this;
    return function(){
        return Function.prototype.call.apply(self,arguments);
    }
}
複製代碼
函數節流
  • 緣由:某些場景下函數被頻繁調用,形成性能問題。例如resize,mousemove,scroll事件
  • 原理:下降函數被調用的頻率
  • 實現:延時器或者時間戳
var throttle = function(fn,interval){
    var self = fn,
        timer,
        firstTime = true;
    return function(){
        var  that = this,
            args = arguments;
        if(firstTime){ //若是第一次被調用,不須要延遲
            self.apply(that, args);
            firstTime = false;
            return;
        }
        if(timer){ //若是定時器還在,說明前一次的延遲執行尚未完成
            return;
        }
        timer = setTimeout(function(){
            clearTimeout(timer);
            timer = null;
            self.apply(that, args)
        },interval || 500);
    }
}

var throttle2 = function(fn,interval){
    var self = fn,
        firstTime = true,
        pre;
    return function(){
        var  that = this,
            args = arguments;
        if(firstTime){ //若是第一次被調用,不須要延遲
            self.apply(that, args);
            firstTime = false;
            pre = new Date().getTime();
            return;
        }
        var now = new Date().getTime();
        if(now - pre > interval){
            pre = now;
            self.apply(that, args);
        }

    }
}

window.onresize = throttle(function(){
    console.log(new Date());
},1000);

複製代碼
分時函數

有些函數確實須要調用屢次。例如手動建立一個用戶數量很大的用戶列表,這樣形成短期在頁面添加大量的dom元素節點,這樣對瀏覽器的性能形成很大影響,形成瀏覽器卡頓甚至卡死的現象。 解決方法:寫一個函數建立節點的工做分批執行,例如200ms建立8個。

下面是一個通用的分時函數

var timeChunk = function(ary,fn,count){
    count = count || 1;
    var timer;
    var start = function(){
        for(var i=0,len=Math.min(count,ary.length);i<len;i++){
            var obj = ary.shift();
            fn(obj);
        }
    };
    return function(){
        timer = setInterval(function(){
            if(ary.length===0){
                clearInterval(timer);
                return;
            }
            start();
        },200);
    }
}

複製代碼
惰性加載函數

有些函數用到的時候只讓它進行加載一次。 好比,瀏覽器的兼容性致使有些方法是不一致的,因此瀏覽器的嗅探工做是不可避免的。

例如經常使用的事件綁定函數。

var addEvent = function(ele,type,handler){
    if(window.addEventListener){
        return ele.addEventListener(type,handler,false);
    }else if(window.attachEvent){
        return ele.attachEvent('on'+type,handler);
    }
}
複製代碼

缺點是每次執行的時候都會執行一次if分支,形成了一些沒有沒必要要的浪費。

改進:把嗅探工做提到代碼加載的時候,在代碼加載的時候就進行一次判斷,這樣使用屢次也會只有一次判斷了。

var addEvent = (function(){
    console.log('addEvent enter')
    if(window.addEventListener){
        return function(ele,type,handler){
            ele.addEventListener(type,handler,false);
        }
    }else if(window.attachEvent){
        return function(ele,type,handler){
            ele.attachEvent('on'+type,handler);
        }
    }
})();

複製代碼

缺點是若是頁面沒有使用到addEvent函數,這樣就形成了浪費,提早嗅探瀏覽器的操做屬於多餘。

改進:使用惰性加載函數。

var addEvent = function(ele,type,handler){
    if(window.addEventListener){
        addEvent = function(ele,type,handler){
            ele.addEventListener(type,handler,false);
        }
    }else if(window.attachEvent){
        addEvent = function(ele,type,handler){
            ele.attachEvent('on'+type,handler);
        }
    }
    return addEvent;
}
複製代碼

這就是惰性載入函數。addEvent被聲明爲一個普通函數,在進入判斷的時候,addEvent被重寫。在下一次進來的時候addEvent函數就不會存在條件分支的語句了。


歡迎計算機前端相關領域小夥伴加入咱們,具體的招聘信息可進入公衆號查看,歡迎關注。

關注咱們吧.jpg

本文由博客一文多發平臺 OpenWrite 發佈!

相關文章
相關標籤/搜索