JavaScript function 簡介
要定義 JavaScript function, 最基本的方法就是直接用
function keyword 來定義
function someFunc() {
// ...
}
這樣做的話, 其實等同於定義一個叫
someFunc 的 variable, 並指定一個 function object 作 value:
var someFunc = function () {
// ...
};
就因為 function 的名稱其實就是一個 variable, 我們可以把它用作另一個 function 的 parameter 甚至是從一個 function return 出去. 細想一下, 在 JavaScript 設定 event handler 時就正正是利用到這一點:
window.onload = function() { alert("Hi!"); };
當然, 除了用作 event handler 之外還有很多用途, 例如我可以寫一個這樣的 function :
function each(inputArray, inputFunction) {
var i;
for(i = 0; i<inputArray.length; ++i) {
inputFunction(inputArray[i]);
}
};
這個 function 就可以指定一個 function 去處理一個 array 的每一個項目, 例如我可以這樣用:
each([ 1, 2, 3 ], function(item) { document.write(item); });
類似的工具在不同的 JavaScript library 都有提供, 只要能夠充份的運用它們, 就能快速地完成想做的工作.
例如在 prototype library 之中我可以這樣寫:
var squares = [ 1, 2, 3 ].collect(function(item) { return item * item; });
// squares = [ 1, 4, 9 ]
當 function 遇上 object
寫 JavaScript 的其中一個良好習慣, 就是把要寫的東西都盡量歸納到自定的 object 之內. 順理成章的, 就會把 function 都定義到 object 之內了. 舉個例子:
var myPopup = {
message: "Hello!",
pop: function () { alert(this.message); }
};
假設當我們希望把它用作某些 event 的 event handler 的話, 很直接的想法就是這樣做:
window.onload = myPopup.pop;
不過這樣做的話不會出現我們所希望的結果. 為甚麼呢? 這是因為當我們把 function 當作 value 使用的時候, 它跟原本 object 之間的關聯就消失了. 想像一下, 以上例子其實跟以下這個一樣的:
window.onload = function () { alert(this.message); };
這樣看的話, 就即時發現
this 在這裡並不是指 myPopup 這個 object 了. (而事實上, 這個 this 在這裡是 JavaScript 的 window object)要解決這個問題, 最容易的做法是定義一個 wrapper function:
window.onload = function() { return myPopup.pop(); };
這樣做就可以肯定
pop function 是經 myPopup 執行的. 要是不喜歡這種 wrapper 的話, 也可以借用一下 object template 的 lexical closure (類似 local scoping):
var PopupClass = function (message){
this.message = message;
var thisObject = this;
this.pop = function () { alert(thisObject.message); }
};
var myPopup = new PopupClass("Hello");
window.onload = myPopup.pop;
很複雜呢! 這裡做的, 是令到
pop function 可以用 thisObject 這個內部的 variable 去使用原來用不到的 this. 這樣做就可以用回 window.onload = myPopup.pop; 這種比較好看的寫法了.再看看另一個情況, 如果這次是把 function 用作另一個 function 的 parameter 呢?
var myPopup = {
message: "Hello ",
pop: function (name) { alert(this.message + name); }
};
function popname(popFunc, name) {
popFunc(name);
}
popname(myPopup.pop, "Mary"); // undefinedMary
popname(myPopup.pop, "Tom"); // undefinedTom
同樣道理, 我們可以用 wrapper 解決:
popname(function() { myPopup.pop("Mary"); });
popname(function() { myPopup.pop("Tom"); });
但你會發現, 每次用到這個 function 的時候都要加一個 wrapper, 很麻煩又容易出錯. 我們可以預先定義一個工具, 再利用 function object 本身的
apply function:
function bind(func, object) {
return function() { return func.apply(object, arguments); };
}
popname(bind(myPopup.pop, myPopup), "Mary");
popname(bind(myPopup.pop, myPopup), "Tom");
同一個 function 更可以在程式中重覆使用. 除了
apply 之外, 還有類似的 call, 但在這個例子不適用.JavaScript library
大部份的 JavaScript library 都提供了相關的工具, 相當方便. 例如 prototype 就直接定義了一個
bind function:
popname(myPopup.pop.bind(myPopup), "Mary");
而 jQuery 就有
proxy function 可以用:
popname($.proxy(myPopup.pop, myPopup), "Mary");
function binding 在 JavaScript 之中是個很重要的概念, 不過同一時間, 亦是一個很容易過份使用的工具. 對於初學者來說, 最重要的是先弄清楚甚麼時候需要用到. 熟習之後才研究甚麼時候需要/不需要用吧.
(BTW, 在這裡還要說清一點, 在 OOP 的世界, 我應該說 "method" 而不是 "function". 不過為免令到一些對 OOP 認識比較少的讀者看得一頭霧水, 還是決定用 "function" 一字)
