《javascript設計模式與開發實踐》閱讀筆記(3)—— 高階函數的其餘應用

高階函數的其餘應用

1.currying

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

var cost = (function(){
    var args = [];
    return function(){
        if ( arguments.length === 0 ){
            var money = 0;
            for ( var i = 0, l = args.length; i < l; i++ ){
                money += args[ i ];    
            }
            return money;
        }else{
            [].push.apply( args, arguments );
        }
    }
})();
                    
cost( 100 ); // 未真正求值
cost( 200 ); // 未真正求值
cost( 300 ); // 未真正求值
console.log( cost() ); // 求值並輸出:600

帶參數不求值 不帶參數求值java

    var currying = function( fn ){
        var args = [];
        return function(){
            if ( arguments.length === 0 ){
                return fn.apply( this, args );
            }else{
                [].push.apply( args, arguments );
                //arguments.callee 在哪個函數中運行,它就表明哪一個函數,主要用在匿名函數中
                return arguments.callee;     
            }
        }
    };

    var cost = (function(){
        var money = 0;
        return function(){
            for ( var i = 0, l = arguments.length; i < l; i++ ){
                money += arguments[ i ];
            }
            return money;
        }
    })();

    var cost = currying( cost ); // 轉化成currying 函數  cost函數有對參數的集中處理

    cost( 100 ); // 未真正求值
    cost( 200 ); // 未真正求值
    cost( 300 ); // 未真正求值
    alert ( cost() ); // 求值並輸出:600

第二個例子更爲通用,把柯里化的過程抽離了出來。設計模式

2.uncurrying

柯里化的過程是逐步傳參,逐步縮小函數的適用範圍,逐步求解的過程。而反柯里化的做用在與擴大函數的適用性,使原本做爲特定對象所擁有的功能的函數能夠被任意對象所用。數組

obj.func(arg1, arg2)  =====>  func(obj, arg1, arg2)瀏覽器

動態語言比較容易實現這一點,下面是實現方式之一:閉包

    var uncurrying= function (fn) {
        return function () {
            var args=[].slice.call(arguments,1);
            return fn.apply(arguments[0],args);        
        }    
    };    

    
    var test="a,b,c";
    var split_new=uncurrying( String.prototype.split );

    test.split(",");       //["a","b","c"]
    split_new(test,',');   //["a","b","c"]            

3.函數節流

減小沒必要要的函數自主調用,好比window.onresize事件  mousemove的拖曳事件等app

 

實現方法之一:函數

將即將被執行的函數用setTimeout延遲一段時間執行。若是該次延遲執行尚未完成,則忽略接下來調用該函數的請求。函數接受2個參數,第一個參數爲須要被延遲執行的函數,第二個參數爲延遲執行的時間。性能

var throttle = function ( fn, interval ) {
    var __self = fn, // 保存須要被延遲執行的函數引用
    timer, // 定時器
    firstTime = true; // 是不是第一次調用

    return function () {
        var args = arguments,
        __me = this;
        if ( firstTime ) { // 若是是第一次調用,不需延遲執行
            __self.apply(__me, args);
            return firstTime = false;
        }
        if ( timer ) { // 若是定時器還在,說明前一次延遲執行尚未完成
            return false;
        }
        timer = setTimeout(function () { // 延遲一段時間執行
            clearTimeout(timer);
            timer = null;
            __self.apply(__me, args);
        }, interval || 500 );
    };
};

window.onresize = throttle(function(){
    console.log( 1 );
}, 500 );

4. 分時函數

數據過大時,好比好友列表中有上千的好友須要渲染,瀏覽器只會一口氣所有去渲染出來,這頗有可能佔據大量的性能,形成嚴重卡頓。這裏能夠人爲控制,將1000ms渲染1000個變成200ms渲染8個,分批渲染,減少壓力this

var timeChunk = function( ary, fn, count ){
    var obj,
        t;
    var len = ary.length;
    var start = function(){
        for ( var i = 0; i < Math.min( count || 1, ary.length ); i++ ){  //肯定執行的數量,而後遍歷執行
            var obj = ary.shift();  //取出第一個元素
            fn( obj );              //執行函數
        }
    };

    return function(){
        t = setInterval(function(){
            if ( ary.length === 0 ){ // 若是數組長度爲0,表示結束
                return clearInterval( t );
            }
            start();
        }, 200 ); // 分批執行的時間間隔,也能夠用參數的形式傳入
    };
};

var ary = [];
for ( var i = 1; i <= 1000; i++ ){
    ary.push( i );
};  //這裏是隨便給一些數據,數據一共1000個 

var renderFriendList = timeChunk( ary, function( n ){
    var div = document.createElement( 'div' );
    div.innerHTML = n;
    document.body.appendChild( div );
}, 8 );

renderFriendList();

4.惰性加載函數

在Web 開發中,由於瀏覽器之間的實現差別,一些嗅探工做老是不可避免。好比咱們須要一個在各個瀏覽器中可以通用的事件綁定函數addEvent,常見的寫法以下:

var addEvent = function( elem, type, handler ){
    if ( window.addEventListener ){
        return elem.addEventListener( type, handler, false );
    }
    if ( window.attachEvent ){
        return elem.attachEvent( 'on' + type, handler );
    }
};

這個函數的缺點是,當它每次被調用的時候都會執行裏面的if 條件分支,形成沒必要要的性能浪費。

 

第二種方案以下,把它提早到到代碼加載的時候,自執行一次,只調用一次判斷,把內部的邏輯預先肯定好。

var addEvent = (function(){
    if ( window.addEventListener ){
        return function( elem, type, handler ){
            elem.addEventListener( type, handler, false );
        }
    }
    if ( window.attachEvent ){
        return function( elem, type, handler ){
            elem.attachEvent( 'on' + type, handler );
        }
    }
})();

這種方案也有個問題,也許咱們從頭至尾都不會使用addEvent函數,這一步就是個多餘操做。

 

第三種方案就是惰性載入函數方案,addEvent依然被聲明爲一個普通函數,在函數裏依然有一些分支判斷。可是在第一次進入條件分支以後,在函數內部會重寫這個函數,重寫以後的函數就是咱們指望的addEvent函數,在下一次進入addEvent函數的時候,addEvent函數裏再也不存在條件分支語句:

 1 var addEvent = function( elem, type, handler ){
 2     if ( window.addEventListener ){
 3         addEvent = function( elem, type, handler ){           //和前面方案的區別在於這裏重寫了本身
 4             elem.addEventListener( type, handler, false );
 5         }
 6     }else if ( window.attachEvent ){
 7         addEvent = function( elem, type, handler ){
 8             elem.attachEvent( 'on' + type, handler );
 9         }
10     }
11     addEvent( elem, type, handler );      //這裏只會第一次執行一次,後面都是直接執行重寫後的
12 };
13 
14 
15 var div = document.getElementById( 'div1' );
16 
17 addEvent( div, 'click', function(){
18     alert (1);
19 });
20 addEvent( div, 'click', function(){
21     alert (2);
22 });

 

由於javascript自身的特色,閉包和高階函數應用極多,在JavaScript中,不少設計模式都是經過閉包和高階函數實現的。很重要的一點是,相對於模式的實現過程,咱們更關注的是模式能夠幫助咱們完成什麼

相關文章
相關標籤/搜索