JavaScript 是一種函式語言(functional language),也就是說它是透過撰寫函式來解決問題,函式是程式執行的基本單位,除了全域程式碼之外,我們寫的所有程式碼都位於函式之內,所以理解函式對精通 JavaScript 很重要。
有幾個觀念要請你先記住:
這是什麼意思呢?在程式設計裡頭等公民是指這種資料類型具有優越性,別人能做的,它也能做得到!它的特性有:可以任意創建、被變數引用、被函式回傳、或當成參數傳給其它的函式。
好比飛機頭等艙的乘客,整架飛機都可以趴趴走,還有專人服務,搭經濟艙只能苦哈哈待在小小的座位上。
物件(object)身為 JavaScript 基本資料類別之一,我們來看看它是不是有頭等公民特性:
{}
var obj = {};
objArray.push({});
obj.data = {};
function func(obj) {
obj.boo = true;
}
func({});
function returnObj() {
return {};
}
var obj = {};
obj.name = "foo";
這些物件的使用方法你應該相當熟練,也是「物件是頭等公民」的特性讓我們能隨心所欲的使用。
JavaScript 函式也是物件,它是不是也是頭等公民呢?我們來看看:
function func (){}
var fooFunction = function() {};
funcArray.push(function() {});
func.data = function() {};
function call(func) {
func();
}
call(function() {});
function returnFunc() {
return function() {};
}
var fooFunction = function() {};
fooFunction.name = "foo";
函式也通過了測試,所以它也是頭等公民之一。除此之外,函式多了「可以被呼叫」的特性,我們可以呼叫函式來執行某項操作,因此函式也是一種特別的物件。
為什麼我們要花大篇幅談論頭等公民的資格?因為頭等公民「可以作為參數傳給函式」的特性,讓我們可以預備一段函式,然後在某個適當的時間點呼叫它。
在「事件處理」我們知道瀏覽器的事件不是按照順序發生,要等到事件的條件成立了(使用者按了按鈕、收到伺服器回傳的資料等),才執行事件函式。一個函式被另一個函式呼叫的情形,在程式設計術語裡叫做「回呼(call back)」。
或許你沒有查覺,當一開始在學 JavaScript 時,你就已經在使用 callback 了!回顧一下我們在生命週期的範例:
document.body.addEventListener("mousemove", function() {
var second = document.getElementById("second");
addMessage(second, "Event: mousemove);
});
原生的addEventListener
就是一個 callback function,它將一個匿名函式當做參數,在mousemove
事件發生時呼叫它。
函式作為物件之一,也擁有自己的屬性,這一點可能會讓不少人感到驚訝。
function func(){};
func.greeting = "Hello";
console.log(func.greeting);
// "Hello"
舉另外一個例子好了,[1, 2, 3].length
我們都知道是3
,用得很習慣,卻沒發現.length
是陣列物件的屬性之一。所以函式具有屬性也是很自然的。
函式屬性的用處是可以在執行當中引用自己來存取屬性的值,同時做簡單的計算和資料儲存,不必另外寫全域變數,也會隨著函式消滅而消失,釋放記憶體。
來看一個簡單的例子,我最近辦了一張網購有高現金回饋的信用卡,於是開始找一些消費方式的組合,讓現金回饋最大化。我找到的方法是用第三方支付綁信用卡,網購時透過返現網站以第三方支付訂購,這樣同時可以拿到信用卡、第三方支付和返現網站的回饋。
function rebate(amount){
// 先檢查是否有 payment 屬性,沒有的話建立屬性並設為0
if(!rebate.payment){
rebate.payment = 0;
}
// 以輸入金額計算每個管道可得到的回饋金,並四捨五入
var card = Math.round(amount * 0.05);
var site = Math.round(amount * 0.01);
var mobilePay = Math.round(amount * 0.02);
// 將原有的回饋金加上新增的回饋金
rebate.payment = rebate.payment + card + site + mobilePay;
return rebate.payment;
}
然後我只要將我的消費金額存成一組陣列,把函式丟到迴圈裡跑,最後我就能得到回饋金的數量。
var arr = [300, 310, 110, 4526, 800];
for(var i = 0; i < arr.length; i++){
rebate(arr[i]);
}
console.log(rebate.payment);
// 484
你看,這樣子484很方便啊!