惰性函數(shù)
惰性載入表示函數(shù)執(zhí)行的分支只會在函數(shù)第一次調(diào)用的時(shí)候執(zhí)行,在第一次調(diào)用過程中,該函數(shù)會被覆蓋為另一個(gè)按照合適方式執(zhí)行的函數(shù),這樣任何對原函數(shù)的調(diào)用就不用再經(jīng)過執(zhí)行的分支了。
例如:為了兼容各瀏覽器,對事件監(jiān)聽的支持:
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;
}
}
上面是注冊函數(shù)緝拿提供的個(gè)瀏覽器兼容函數(shù)。由于各瀏覽器之間的差異,不得不在用的時(shí)候做能力檢測。顯然,單從功能上講,已經(jīng)做到了兼容各瀏覽器。但是每次監(jiān)聽,都會做一次能力檢測,這就沒必要了,真正的應(yīng)用中,這顯然是多余的,對同一個(gè)應(yīng)用環(huán)境中,只需做一次檢測即可。
于是做如下改變:
<!DOCTYPE html>
<html>
<head>
<title></title>
<style type="text/css">
#app{
width: 50px;
height: 50px;
background-color: skyblue;
}
</style>
</head>
<body>
<div id="app"></div>
<script type="text/javascript">
function addEvent(element,type,fun) {
if(element.addEventListener){
addEvent = function(element,type,fun){
console.log('chrome addEvent');
element.addEventListener(type,fun,false);
}
}else if(element.attachEvent){
addEvent = function(element,type,fun){
element.attachEvent('on'+type,fun);
}
}else{
addEvent = function(element,type,fun){
element['on'+type] = fun;
}
}
return addEvent(element,type,fun);
}
var app = document.getElementById("app");
addEvent(app,'click',function(){
alert(1);
})
addEvent(app,'click',function(){
alert(2);
})
</script>
</body>
</html>
可以看出,第一次調(diào)用addEvent會對瀏覽器做能力檢測,然后重寫了addEvent。下次函數(shù)被重寫,不會再做能力檢測了。
類似的經(jīng)典例子還有:XMLHttpRequest
function createXHR() {
var xhr;
if(typeof XMLHttpRequest != 'undefined') {
xhr = new XMLHttpRequest();
createXHR = function() {
return new XMLHttpRequest();
}
}else {
try {
xhr = new ActiveXObject("Msxml2.XMLHTTP");
createXHR = function() {
return new ActiveXObject("Msxml2.XMLHTTP");
}
}catch(e) {
try {
xhr = new ActiveXObject("Microsoft.XMLHTTP");
createXHR = function() {
return new ActiveXObject("Microsoft.XMLHTTP");
}
}catch(e) {
createXHR = function () {
return null;
}
}
}
}
return xhr
}
代碼中,我們讓函數(shù)在第一次運(yùn)行之后,則判斷除了瀏覽器的環(huán)境,就被重新賦值了。賦值后的函數(shù)是直接return對應(yīng)的方法。所以這個(gè)函數(shù)需要第二次調(diào)用的時(shí)候才真正的被調(diào)用。
正式因?yàn)樗诙握{(diào)用的餓時(shí)候,沒有去走第一次調(diào)用那樣復(fù)雜的判斷的路,才顯得“懶惰”。因此我們叫惰性函數(shù)。
惰性函數(shù)應(yīng)用場景
1、應(yīng)用頻繁,如果只用一次,是體現(xiàn)不出來它的優(yōu)點(diǎn)的,用的次數(shù)越多,越能體現(xiàn)這種模式的優(yōu)勢所在;
2、固定不變,一次判定,在固定的應(yīng)用環(huán)境中不會發(fā)生改變;
函數(shù)柯里化
函數(shù)柯里化又稱部分求值,一個(gè)柯里化的函數(shù)首先會接受一些參數(shù),接受了這些參數(shù)之后,該函數(shù)并不會立即求值,而是繼續(xù)返回另外一個(gè)函數(shù),剛才傳入的參數(shù)在函數(shù)形成的閉包中被保存起來。待到函數(shù)被真正需要求值的時(shí)候,之前傳入的所有參數(shù)都會被一次性用于求值。
var currying = function(fn){
var args = [];
return function(){
if(arguments.length === 0){
return fn.apply(this,args);
}else{
[].push.apply(args,arguments);
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); // 轉(zhuǎn)化成 currying 函數(shù)
cost(100)(200); // 未真正求值
cost(200); // 未真正求值
cost(300); // 未真正求值
console.log(cost()); // 求值并輸出:800
以上代碼中,完成了一個(gè)currying函數(shù)的編寫。當(dāng)調(diào)用cost()時(shí),如果明確地帶上了一些參數(shù),表示此時(shí)并不進(jìn)行真正的求值計(jì)算,而是把這些參數(shù)保存下來,此時(shí)讓cost函數(shù)返回另外一個(gè)函數(shù)。只有以不帶參數(shù)的而形式執(zhí)行cost()時(shí),才利用前面保存的所有參數(shù),真正開始進(jìn)行求值計(jì)算。