JavaScript的函數

很隨意的總結,幾乎是代碼片斷,純粹當我的筆記。html

 

JavaScript中,定義Function對象有兩種方法。express

函數聲明(function declaration)編程

1 function fn() {
2     // 代碼
3 };

函數表達式(function expression)閉包

var fn = function () {
    // 代碼
};

 

 

函數做回調(callback)時的做用域

其實回調並非傳遞一次性的匿名函數或全局函數,而是對象的方法。
看下面的代碼,輸出結果和咱們所想的不一致。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)。
當函數執行的那一刻纔會進行「初始化」,從新去定義函數。

 

 

即時函數(immediate function)

什麼是即時函數?顧名思義,就是馬上執行的函數。

下面三種都是即時函數的寫法:

 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...

 

 

即時對象初始化(immediate object initialization)

若是你不喜歡即時函數的方式,也能夠換一個形式:

 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 的「高級模式」才能對其進行「變量名壓縮」。

 

 

柯里化(Currying)

在純粹的函數式編程語言中,函數並不描述爲被調用(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

什麼時候使用柯里化?
當發現同一個函數,被調用屢次,而且每次傳遞的參數絕大多數是相同的,這個時候應該用柯里化。

 

以上。

參考:《JavaScript設計》

 

 

本文做者:Maple Jan

本文連接:http://www.cnblogs.com/maplejan/archive/2013/06/17/3140004.html

相關文章
相關標籤/搜索