關於js的高級函數(惰性函數,函數柯里化,級聯函數)


《1》首先是惰性函數

惰性載入表示函數執行的分支只會在函數第一次掉用的時候執行,在第一次調用過程中,該函數會被覆蓋為另一個按照合適方式執行的函數,這樣任何對原函數的調用就不用再經過執行的分支了。

看下面典型的例子:

為了兼容各瀏覽器,對事件監聽的的支持:

function addEvent (type, element, fun) {
    if (element.addEventListener) {
        element.addEventListener(type, fun, false);
    }
    else if(element.attachEvent){
        element.attachEvent('on' + type, fun);
    }
    else{
        element['on' + type] = fun;
    }
}

上面是注冊函數監聽的各瀏覽器兼容函數。由於,各瀏覽之間的差異,不得不在用的時候做能力檢測。顯然,單從功能上講,已經做到了兼容各瀏覽器。但是,每次綁定監聽,都會對能力做一次檢測,這就沒有必要了,真正的應用中,這顯然是多余的,同一個應用環境中,只需要檢測一次即可。

於是有了如下改變:

function addEvent (type, element, fun) {
    if (element.addEventListener) {
        addEvent = function (type, element, fun) {
            element.addEventListener(type, fun, false);
        }
    }
    else if(element.attachEvent){
        addEvent = function (type, element, fun) {
            element.attachEvent('on' + type, fun);
        }
    }
    else{
        addEvent = function (type, element, fun) {
            element['on' + type] = fun;
        }
    }
    return addEvent(type, element, fun);
}

可以看出,第一次調用addEvent會對瀏覽器做能力檢測,然后,重寫了addEvent。下次再調用的時候,由於函數被重寫,不會再做能力檢測。

類似的例子還有:XMLHttRequest:

 

function createXHR() {  4 var xhr;  5 try{  6 xhr = new XMLHttpRequest();  7 }catch(e) {  8  handleErr(e);  9 10 try { 11 xhr = new ActiveXObject("Msxml2.XMLHTTP"); 12 }catch(e) { 13 try{ 14 xhr = new ActiveXObject("Microsoft.XMLHTTP"); 15 }catch(e) { 16 xhr = null; 17  } 18  } 19  } 20 21 return xhr ; 22 } 23 24 function handleErr(error) { 25 // 這一步在實戰中很重要,因為catch會延長作用域鏈,所以是在全局作用域聲明的e 26 // 這里我們把它賦給局部變量,則查找更快 27 var err = error; 28 29 // do sth. 30 }

 

由於不同的瀏覽器的xmlhttpreque對象的不同形式,為了兼容同樣要在使用ajax時判斷,但同樣這樣的判斷只需一次。

看惰性函數優化后:

 

function createXHR() {  5 var xhr;  6 if(typeof XMLHttpRequest != 'undefined') {  7 xhr = new XMLHttpRequest();  8 createXHR = function() {  9 return new XMLHttpRequest(); 10  } 11 }else { 12 try { 13 xhr = new ActiveXObject("Msxml2.XMLHTTP"); 14 createXHR = function() { 15 return new ActiveXObject("Msxml2.XMLHTTP"); 16  } 17 }catch(e) { 18 try { 19 xhr = new ActiveXObject("Microsoft.XMLHTTP"); 20 createXHR = function() { 21 return new ActiveXObject("Microsoft.XMLHTTP"); 22  } 23 }catch(e) { 24 createXHR = function () { 25 return null; 26  } 27  } 28  } 29  } 30 return xhr 31 }

代碼中,我們讓函數在第一次運行之后,則判斷除了瀏覽器的環境,就被重新賦值了。
賦值后的函數是直接return 對應的方法。所以,這個函數,需要第二次調用的時候才真正的被調用。
正是因為它第二次調用函數的時候,沒有去走第一次調用那樣復雜的判斷的路,所以顯得“懶惰”。因此我們叫它 惰性函數

可以總結出在哪些情況下使用惰性函數:

1 應用頻繁,如果只用一次,是體現不出它的優點出來的,用的次數越多,越能體現這種模式的優勢所在;

 
        

2 固定不變,一次判定,在固定的應用環境中不會發生改變;

 

《2》函數柯里化

關於這個看了好多資料,其實還是並沒有很懂,<攤手>

直接上代碼:

// //函數柯里化

1》提高適用性:

【通用函數】解決了兼容性問題,但同時也會再來,使用的不便利性,不同的應用場景往,要傳遞很多參數,以達到解決特定問題的目的。有時候應用中,同一種規則可能會反復使用,這就可能會造成代碼的重復性。

function square(i) {
    return i * i;
}

function dubble(i) {
    return i *= 2;
}

function map(handeler, list) {
    return list.map(handeler);
}

// 數組的每一項平方
map(square, [1, 2, 3, 4, 5]);
map(square, [6, 7, 8, 9, 10]);
map(square, [10, 20, 30, 40, 50]);
// ......

// 數組的每一項加倍
map(dubble, [1, 2, 3, 4, 5]);
map(dubble, [6, 7, 8, 9, 10]);
map(dubble, [10, 20, 30, 40, 50]);

函數柯里化改造后:

function curry(fn){
 var args=Array.prototype.slice.call(arguments,1);
 console.log(args);//輸出:function aquare(){...}
 return function(){
  var innerArgs=Array.prototype.slice.call(arguments);
  console.log(innerArgs);//輸出:1,2,3,4,5
  var finalArgs=args.concat(innerArgs);
  console.log(finalArgs);//輸出:function square(){},1,2,3,4,5
  return  fn.apply(null,finalArgs);
 }
}

function square(i) {     return i * i; }

function dubble(i) {     return i *= 2; }

function map(handeler, list) {     return list.map(handeler); }

var mapSQ = curry(map, square);

mapSQ([1, 2, 3, 4, 5]);

mapSQ([3,2,5,7,6,7]);

var mapDB = curry(map, dubble);

mapDB([1, 2, 3, 4, 5]);

mapDB([2,5,7,6,2,3]);

柯里化通常也稱部分求值,其含義是給函數分步傳遞參數,每次傳遞參數后部分應用參數,並返回一個更具體的函數接受剩下的參數,這中間可嵌套多層這樣的接受部分參數函數,直至返回最后結果。
因此柯里化的過程是逐步傳參,逐步縮小函數的適用范圍,逐步求解的過程。

var currying = function (fn) {
    var _args = [];
    return function () {
        if (arguments.length === 0) {
            return fn.apply(this, _args);
        }
        Array.prototype.push.apply(_args, [].slice.call(arguments));
        return arguments.callee;
    }
};

var multi=function () {
    var total = 0;
    for (var i = 0, c; c = arguments[i++];) {
        total += c;
    }
    return total;
};

var sum = currying(multi); 
 
sum(100,200)(300);
sum(400);
console.log(sum());     // 1000  (空白調用時才真正計算)

上面的代碼其實是一個高階函數(high-order function), 高階函數是指操作函數的函數,它接收一個或者多個函數作為參數,並返回一個新函數。此外,還依賴與閉包的特性,來保存中間過程中輸入的參數。即:

  • 函數可以作為參數傳遞
  • 函數能夠作為函數的返回值
  • 閉包

柯里化的作用

  • 延遲計算。上面的例子已經比較好低說明了。
  • 參數復用。當在多次調用同一個函數,並且傳遞的參數絕大多數是相同的,那么該函數可能是一個很好的柯里化候選。
  • 動態創建函數。這可以是在部分計算出結果后,在此基礎上動態生成新的函數處理后面的業務,這樣省略了重復計算。或者可以通過將要傳入調用函數的參數子集,部分應用到函數中,從而動態創造出一個新函數,這個新函數保存了重復傳入的參數(以后不必每次都傳)。例如,事件瀏覽器添加事件的輔助方法:

var addEvent = function(el, type, fn, capture) {
     if (window.addEventListener) {
         el.addEventListener(type, function(e) {
             fn.call(el, e);
         }, capture);
     } else if (window.attachEvent) {
         el.attachEvent("on" + type, function(e) {
             fn.call(el, e);
         });
     }
};

每次添加事件處理都要執行一遍 if...else...,其實在一個瀏覽器中只要一次判定就可以了,把根據一次判定之后的結果動態生成新的函數,以后就不必重新計算。

var addEvent = (function(){
    if (window.addEventListener) {
        return function(el, sType, fn, capture) {
            el.addEventListener(sType, function(e) {
                fn.call(el, e);
            }, (capture));
        };
    } else if (window.attachEvent) {
        return function(el, sType, fn, capture) {
            el.attachEvent("on" + sType, function(e) {
                fn.call(el, e);
            });
        };
    }
})();

 

《3》級聯函數

!--JavaScript級聯函數-->
<!--本課時介紹JavaScript級聯函數,
級聯函數也叫鏈式函數,方法鏈一
般適合對一個對象進行連續操作
(集中在一句代碼)。一定程度上
可以減少代碼量,缺點是它占用了
函數的返回值。-->

function myclassA(){
 this.name="";
 this.age="";
 this.sex="";
}
myclassA.prototype={
 setname:function(){
  this.name="katherine";
  return this;
 },
 setage:function(){
  this.age="22";
  return this;
 },
 setsex:function(){
  this.sex='girl';
  return this;
 }
}
var me =new myclassA();
console.log(me.setname().setage().setsex());

// myclassA {name: "katherine", age: "22", sex: "girl"}

 

參考自http://www.tuicool.com/articles/N7Z3qey,

參考:

http://sombie.diandian.com/post/2013-06-28/40050585369

http://book.2cto.com/201211/9320.html

http://zh.wikipedia.org/wiki/Currying

http://www.ibm.com/developerworks/cn/web/1006_qiujt_jsfunctional/

http://www.cnblogs.com/lwbqqyumidi/archive/2012/12/03/2799833.html

 

純屬個人學習參考資料

 
        

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM