iT邦幫忙

第 11 屆 iThome 鐵人賽

DAY 5
1
Modern Web

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

Day 05: 函式的定義方式

JavaScript 提供了四個建立函式的方式:

  • 函式宣告(declaration)和函式表達式(expression)
  • 箭頭函式(arrow function)
  • 函式建構式(function constructor)
  • 生成器(generator)

Function declaration 和 function expression 是最常見的函式定義方式,它們看起來很像,但有些微的不同,將在這篇文章裡探討。

Arrow function 則是 ES6 的新功能,它能夠用更簡潔的語法建立函式。

Generator 後面我們會講到,現在只要知道它也是 ES6 的新增功能就好了。

Function constructor不常見,它能動態的從字串建立函式,但只能夠建立全域函式,這就無法利用 JavaScript 的重要特性—closure,所以非常少機會使用,我們也就不花費時間研究。

var sum = new Function('a', 'b', 'return a + b');
console.log(sum(2,3));
// 5

Function declaration

Function declaration 是最基本的函式定義方式,它的結構嚴謹,必須包含以下的部份:

  • 以必要的function關鍵字開頭
  • 必要函式名稱、括號()和大括號{}
  • 程式碼
  • 必須是獨立的程式區塊,但是函式可以在其他函式之內,又稱巢狀函式(nested function)
// 宣告一個全域函式
function outterFunction() {
	// 宣告一個內部函式
	function innerFunction() {
		return "Hello from innerFunction!";
	}
	return innerFunction();
}
// 呼叫函式的方法
outterFunction();

使用巢狀函式時,要注意變數和函式名稱的範圍(scope),這一點將在後面的篇幅說明。

Function expression

前一篇文章我們談論過函式具有 first-class object 的特性,讓函式如同其他 JavaScript 基礎資料類型(Number, String等)一樣擁有任意建立、指派、傳遞和回傳的特權。

我們可以指派數字和字串給變數,也可以將數字或字串當成函式的參數。

var foo = 3;
var bar = "bar";
func(5){
	return 10;
};

這樣的寫法在計算機程式裡叫做「表達式(expression)」。Expression 的結果一定會產生一個值,3bar5都是值。

我們也可以在相同的位置使用函式。

var foo = function(){};
func(function(){}) {
	return function() {};
};

因此這種建立函式的方法就叫做 function expression,在 JavaScript 引擎預期會看到值的地方建立函式,該函式就會被判定成 expression(值),例如在運算子的右邊、函式的參數,或是函式的回傳值。

Function expression 和 function declaration 最大的差別是 function expression 可以不要有名稱,沒有名稱的函式又叫匿名函式(anonymous function)。

另一個差異是函式名稱「提升(hoisting)」的現象,這一點後面會有一篇文章專門說明。

如果 function expression 被指派給一個變數,可以使用該變數來進行呼叫。

var func = function() {};
func();

// Function expression 的函式名稱可有可無,
// 如果有名稱的話,就可以在函式內用該名稱存取函式本身。
// 但是仍然要以被指派的變數來呼叫,不能以函式名稱呼叫。
var bar = function foo() {
	foo.parm = "This is a function parameter.";
	console.log(foo.parm);
};
bar(); // "This is a function parameter."
foo(); // Error: foo is not defined

如果是當成其他函式的參數,則可以用參數名稱來呼叫。

var func = function(foo) {
	foo();
};
func(function() {
	console.log("Hello from another function!");
});

立即函式(immediate function)

除了可以用被指派的變數名稱來呼叫 function expression 之外, 在括號左邊也可以是一段 function expression,如此一來就可以立即呼叫這段剛建好的函式,這種寫法叫做 immediately invoked function expression (IIFE),簡稱立即函式。

// 標準函式呼叫
var square = function(n) {
	return n * n;
};

console.log(square(5));
// 25

// 立即呼叫的 function expression
var result = (function(n) {
	return n * n;
})(5);

console.log(result);
// 25

包圍在 function expression 外的括號叫做 grouping operators,沒有這組括號的話,JavaScript 就會報錯,因為如果沒有括號的話,程式碼就會變成function(){}(5),JavaScript 就會把它當成是一個 function declaration,但是沒有在function關鍵字後面看到函式名稱,於是會回傳錯誤。括號的作用是在告知 JavaScript 引擎「這裡是一段 function expression」。如同在寫數學運算式時,乘除會優先於加減,一旦加了括號就可以先進行括號內的運算。

2 + 3 * 5 = 17
(2 + 3) * 5 = 25

立即函式還有其他種寫法。像是把整個立即函式都包在括號內。

var square = (function(n){
	return n * n;
}(5));

或是在函式前加上一元運算子,這是在告訴 JavaScript 引擎,它處理的是一個 expression。

+function(){ return 3 + 2; }();
-function(){ return 3 + 2; }();
!function(){ return 3 + 2; }();
~function(){ return 3 + 2; }();

立即函式只會執行一次,其他的程式碼無法引用。它的用處在當場計算出一個值來。還有因為 JavaScript 變數範圍的特性,讓立即函式內的變數名稱不會和其他程式碼相同名稱的變數衝突,同時讓 JavaScript 模擬出模組化的功能,這在後面的文章會講到。

Arrow function

Arrow function 是 ES6 新加入的 JavaScript 標準,能讓我們以更簡潔的方式建立函式,我們來看看它的規則。

// 一段 function expression
var saySorry = function(name) {
	return "I'm sorry " + name + ", I'm afraid I can't do that.";
};

首先先省略function關鍵字,改用箭頭符號=>

var saySorry = (name) => {
	return "I'm sorry " + name + ", I'm afraid I can't do that.";
};

如果只有一個參數,括號也可以省略。

var saySorry = name => {
	return "I'm sorry " + name + ", I'm afraid I can't do that.";
};

如果程式碼只有一行,而且是回傳值的話,可以同時省略大括號和return

var saySorry = name => "I'm sorry " + name + ", I'm afraid I can't do that.";

以上的寫法在 JavaScript 裡都是通過的,而且是同樣功能的函式。

如果函式沒有參數,那麼括號就要保留。

var saySorryToDave = () => "I'm sorry Dave, I'm afraid I can't do that.";

回傳值是物件的話,要用括號把物件包起來。

var fullName = (firstName, lastName) => ( {firstName: firstName, lastName: lastName} );

從以上寫法可以看出來,arrow function 其實就是 function expression 的簡化寫法。而 function declaration 在 ES6 中並沒有簡化的方式,所以還是要用function開頭來宣告函式。


上一篇
Day 04: 函式是頭等物件
下一篇
Day 06: 函式的 arguments & parameters
系列文
JavaScript 忍者的修練--從下忍進階到中忍30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言