很隨意的總結,幾乎是代碼片斷,純粹當我的筆記。html
JavaScript中,定義Function對象有兩種方法。express
函數聲明(function declaration)編程
1 function fn() { 2 // 代碼 3 };
函數表達式(function expression)閉包
var fn = function () { // 代碼 };
其實回調並非傳遞一次性的匿名函數或全局函數,而是對象的方法。
看下面的代碼,輸出結果和咱們所想的不一致。app
1 var obj = {}; 2 obj.color = "red"; 3 obj.getColor = function () { 4 return { 5 _this: this, 6 color: this.color 7 } 8 }; 9 10 function printColor(callback) { 11 console.log(callback()); 12 } 13 14 printColor(obj.getColor); // console print {_this: Window, color: undefined}
要解決這個問題,能夠利用call()或者apply():編程語言
1 function printColor(callback, obj) { 2 console.log(callback.call(obj)); 3 } 4 5 printColor(obj.getColor, obj); // console print {_this: Object, color: "red"}
但這種寫法不夠直觀,咱們換一個思路試試:模塊化
1 function printColor(key, obj) { 2 console.log(obj[key]()); 3 } 4 5 printColor("getColor", obj); // console print {_this: Object, color: "red"}
所謂的返回函數,其實就是:函數執行完後返回一個匿名函數。函數式編程
以下:函數
1 function fnA() { 2 alert(1); 3 return function () { 4 alert(2); 5 }; 6 } 7 8 var fnB = fnA(); // alert 1 9 fnB(); // alert 2
實際應用中,可建立一個閉包,用來存儲私有數據:優化
1 function setup() { 2 var count = 0; 3 return function () { 4 return ++count; 5 }; 6 } 7 8 var next = setup(); 9 next(); // return 1 10 next(); // return 2 11 next(); // return 3
我的以爲更好的用法是,結合即時函數:
1 var next = (function () { 2 var count = 0; 3 return function () { 4 return ++count; 5 }; 6 })(); 7 8 next(); // return 1 9 next(); // return 2 10 next(); // return 3
若是結合了即時函數,就能夠作條件預加載(Conditional Advance Loading):·
1 var addEvent = (function (W) { 2 if (W.addEventListener) { 3 return function (target, type, handler) { 4 target.addEventListener(type, handler, false); 5 }; 6 } else if (W.attachEvent) { 7 return function (target, type, handler) { 8 target.attachEvent("on" + type, handler); 9 }; 10 } else { 11 return function (target, type, handler) { 12 target["on" + type] = handler; 13 }; 14 } 15 })(window);
關於返回函數,最大的價值仍是在於建立閉包。若是要存儲私有數據,閉包當然十分好用。
但若是像上述代碼,並不須要存儲私有數據,返回函數就顯得有點多餘。
但咱們依然能夠條件預加載:
1 var addEvent = function () {}; 2 if (window.addEventListener) { 3 addEvent = function (target, type, handler) { 4 target.addEventListener(type, handler, false); 5 }; 6 } else if (window.attachEvent) { 7 addEvent = function (target, type, handler) { 8 target.attachEvent("on" + type, handler); 9 }; 10 } else { 11 addEvent = function (target, type, handler) { 12 target["on" + type] = handler; 13 }; 14 }
只是,這些寫貌似沒那麼炫了...
若是隻有一個判斷條件,建議使用三元運算符,更直觀更簡單:
1 var addEvent = window.addEventListener ? function (target, type, handler) { 2 target.addEventListener(type, handler, false); 3 } : function (target, type, handler) { 4 target.attachEvent("on" + type, handler); 5 };
所謂的自定義函數,能夠理解爲:在函數內部覆蓋(從新定義)函數自身。
1 function fnA() { 2 alert(1); 3 fnA = function () { 4 alert(2); 5 }; 6 } 7 fnA(); // alert 1 8 fnA(); // alert 2
利用自定義函數的特色,咱們能夠對上面的addEvent進行優化:
1 var addEvent = function(target, type, handler) { 2 if (target.addEventListener) { 3 addEvent = function (target, type, handler) { 4 target.addEventListener(type, handler, false); 5 }; 6 } else if (target.attachEvent) { 7 addEvent = function (target, type, handler) { 8 target.attachEvent("on" + type, handler); 9 }; 10 } else { 11 addEvent = function (target, type, handler) { 12 target["on" + type] = handler; 13 }; 14 } 15 addEvent(target, type, handler); 16 };
這種寫法又叫:延遲加載(Lazy Loading) 或 惰性函數定義(Lazy Function Definition)。
當函數執行的那一刻纔會進行「初始化」,從新去定義函數。
什麼是即時函數?顧名思義,就是馬上執行的函數。
下面三種都是即時函數的寫法:
1 (function () { 2 alert(1); 3 }()); 4 5 (function () { 6 alert(2); 7 })(); 8 9 var fn = function() { 10 alert(3); 11 }();
網頁開發中,各類事件是最難抽象的,由於業務邏輯層出不窮。
看下面代碼:
1 // code... 2 var $body = $("body"), 3 MESSAGE = "Hello!"; 4 5 $body.on("click", function() { 6 console.log(MESSAGE); 7 }); 8 // code...
這是一段十分常見的事件綁定的代碼。
就這樣看十分清晰,但若是把它擺在n段事件綁定的代碼之中,估計就會讓人抓狂了。
單單避免變量名衝突這一點,就十分蛋疼了。
利用即時函數讓代碼模塊化:
1 // code... 2 (function () { 3 var $body = $("body"), 4 MESSAGE = "Hello!"; 5 6 $body.on("click", function() { 7 console.log(MESSAGE); 8 }); 9 })(); 10 // code...
若是你不喜歡即時函數的方式,也能夠換一個形式:
1 // code... 2 ({ 3 $body: $("body"), 4 MESSAGE: "Hello!", 5 bindClick: function() { 6 var msg = this.MESSAGE; 7 this.$body.on("click", function() { 8 console.log(msg); 9 }); 10 }, 11 bindEvent: function() { 12 this.bindClick(); 13 } 14 }).bindEvent(); 15 // code...
這種作法,有一個比較大的缺陷是,大部分壓縮器都不會對其進行「變量名壓縮」。
目前只有 Google Closure Compile 的「高級模式」才能對其進行「變量名壓縮」。
在純粹的函數式編程語言中,函數並不描述爲被調用(called或invoked),而是描述爲應用(applied)。
1 alert(1); // 調用函數 2 alert.apply(null, [1]); // 應用函數
柯里化例子:
1 function currying(fn) { 2 var slice = Array.prototype.slice, 3 storedArgs = slice.call(arguments, 1); 4 return function () { 5 var newArgs = slice.call(arguments), 6 args = storedArgs.concat(newArgs); 7 return fn.apply(null, args); 8 }; 9 } 10 11 function add(x, y) { 12 return x + y; 13 } 14 15 add(5, 3); // return 8 16 var add10 = currying(add, 10); 17 add10(11); // return 21
什麼時候使用柯里化?
當發現同一個函數,被調用屢次,而且每次傳遞的參數絕大多數是相同的,這個時候應該用柯里化。
以上。
本文做者:Maple Jan
本文連接:http://www.cnblogs.com/maplejan/archive/2013/06/17/3140004.html