iT邦幫忙

第 11 屆 iThome 鐵人賽

DAY 4
1
Modern Web

JavaScript 忍者的修練--從下忍進階到中忍系列 第 4

Day 04: 函式是頭等物件

  • 分享至 

  • xImage
  •  

頭等物件的特性

JavaScript 是一種函式語言(functional language),也就是說它是透過撰寫函式來解決問題,函式是程式執行的基本單位,除了全域程式碼之外,我們寫的所有程式碼都位於函式之內,所以理解函式對精通 JavaScript 很重要。

有幾個觀念要請你先記住:

  • 「函式是JavaScript的頭等物件(first-class object),或稱頭等公民(first-class citizen)」
  • 「在 JavaScript 裡,所有的東西都是物件(in JavaScript, everything is an object)」

這是什麼意思呢?在程式設計裡頭等公民是指這種資料類型具有優越性,別人能做的,它也能做得到!它的特性有:可以任意創建、被變數引用、被函式回傳、或當成參數傳給其它的函式。

好比飛機頭等艙的乘客,整架飛機都可以趴趴走,還有專人服務,搭經濟艙只能苦哈哈待在小小的座位上。

物件(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";

函式也通過了測試,所以它也是頭等公民之一。除此之外,函式多了「可以被呼叫」的特性,我們可以呼叫函式來執行某項操作,因此函式也是一種特別的物件。

Callback function

為什麼我們要花大篇幅談論頭等公民的資格?因為頭等公民「可以作為參數傳給函式」的特性,讓我們可以預備一段函式,然後在某個適當的時間點呼叫它。

在「事件處理」我們知道瀏覽器的事件不是按照順序發生,要等到事件的條件成立了(使用者按了按鈕、收到伺服器回傳的資料等),才執行事件函式。一個函式被另一個函式呼叫的情形,在程式設計術語裡叫做「回呼(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很方便啊!


上一篇
Day 03: 網頁生命週期之事件處理
下一篇
Day 05: 函式的定義方式
系列文
JavaScript 忍者的修練--從下忍進階到中忍30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言