在 JavaScript 中,函式(Function)
是一種具有「可執行程式碼片段」的物件,與其他物件最大的差別是函式能被呼叫。函式能被當作一般的物件來傳遞和操作,也可以被當作參數傳遞給其他函式等等。接下來就是關於函式的基本介紹。
在 JavaScript 中,函式可以分為 傳統函式
和 箭頭函式(ES6 新增)
。這兩者當然都屬於物件,也都具有函式可被呼叫的特性,但在細節上還是有些地方不同。無論如何,我們先來看定義一個函式的方式。
函式陳述式
// 此函式為具名函式 function greet(name) { console.log(`Hello, ${name}!`); }
函式表達式
// 此函式為匿名名函式 function(name) { console.log(`Hello, ${name}!`); };
箭頭函式
於 ES6 新增,它提供了更簡潔的語法來宣告一個函式。
箭頭函式表達式
// 箭頭函式一定是匿名的 (name) => { console.log(`Hello, ${name}!`); };
以上三個範例都定義了一個函式,該函式接受一個參數 name,並會在呼叫時根據參數執行裡面的程式碼片段。
而函式可以是具名的(named functions)或匿名的(anonymous functions)。這兩者之間的區別在於是否給函式賦予一個名稱。在上述例子中的函式陳述式是具名的,而透過函式表達式定義的函式及箭頭函式都是匿名的。
匿名函式是在函式定義時沒有指定名稱的函式,也就是說無法透過名稱來呼叫這些函式,那「匿名」究竟能帶來甚麼好處呢?實際上,它們通常被用在只有當下被參考,不會在其他地方被呼叫的情況。通常會用在 回調函式(callback functions)
或 立即函式
上。這樣做的優點是讓不會頻繁出現在程式碼的函式不需另外命名,讓程式碼更簡潔。
只看文字或許不易理解,直接來看例子。
const add = (a, b) => a + b;
// `(a, b) => a + b` 本身是一個匿名箭頭函式,它沒有名稱,無法在地方被呼叫。
// 但是 add 參考了此函式,因此還是可以透過 add() 來呼叫
const numbers = [1, 2, 3, 4, 5];
// forEach 中的回調函式通常用匿名函式
numbers.forEach(function(number) {
console.log(number * 2);
});
// setTimeout 中的回調函式通常用匿名函式
setTimeout(() => {
console.log('延遲一秒後執行這裡');
}, 1000);
// 這是立即函式的語法,它在定義後立即執行
(function() {
console.log('Hello!');
})();
閉包(closure)是指在某個函式內部定義的函式,它可以訪問包含它的外部函式的變數。閉包允許你在一個內部函式中訪問外部函式的變數,即使外部函式已經執行完成並回傳了。但是外部函式並不能訪問內部函式的變數與函式。這保障了內部函式的變數安全。
閉包的主要特性是它可以「記住」它被創建在哪個作用域,並且可以訪問那個作用域的變數。這樣的特性使閉包在 JavaScript 中非常強大和有用。
function outerFunction(outerVariable) {
return function(innerVariable) {
console.log(outerVariable + innerVariable);
}
// return innerFunction;
}
const closure = outerFunction(3);
closure(5); // 輸出: 8
在這個例子中,outerFunction 是一個外部函式,它回傳了一個內部函式。當 outerFunction 被呼叫時,它創建了一個閉包,內部函式記住了 outerFunction 的作用域,並且可以訪問 outerFunction 中的 outerVariable。當 closure(5) 被呼叫時,它使用了 outerVariable 的值(3),以及傳遞給它的參數值(5),最終輸出 8。
閉包常常用於保護變數,封裝私有變數,以及在 JavaScript 中實現模組模式。這使得你可以創建私有作用域,避免全局作用域的污染,同時還能夠訪問外部作用域的變數和函數。
今天介紹了 JavaScript 中的 傳統函式
和 箭頭函式
,還有具名與匿名的差別,以及匿名特性的用途,也說明了閉包是甚麼。但了解這些還不夠,要特別注意的是 JavaScript 中的 this
關鍵字。在傳統函式和箭頭函式中, this 的規則是不同的,這就留到明天談吧。