Skip to content

函式 (Function)

在程式設計中,函式是一段可以重複使用的程式碼,用來執行特定的任務。透過函式,我們可以讓程式更具結構性、可讀性和可維護性。

什麼是函式?

函式是一個封裝好的程式碼區塊,可以透過名稱來呼叫它,並執行其中的程式碼。

函式的好處在於:

  1. 重複使用:我們可以多次呼叫同一個函式,而不需要重複撰寫相同的程式碼。
  2. 可讀性:函式名稱可以清楚地表達其功能,讓程式碼更易於理解。
  3. 維護性:如果需要修改某個功能,只需修改函式內部的程式碼,而不需要在多處修改。
  4. 抽象化:將複雜的邏輯封裝起來,讓使用者不需要了解內部實現細節,只需關心如何使用。
  5. 模組化:將程式碼分成小塊,讓整體結構更清晰。
  6. 參數化:接受不同的輸入,讓同一個函式可以處理多種情況。

如何撰寫一個函式:function + 函式名稱 + (輸入的參數) + {函式實際執行的內容},以下是一個簡單的範例:

javascript

// 這裡是一段重複四次的程式碼
console.log(`Welcome, Alice!`)
console.log(`Hello, Bob!`)
console.log(`No, Charlie!`)
console.log(`Nice to meet you, David!`)


// 上面四個程式碼可以整理成一個函式
// 這樣就可以重複使用這段程式碼,只要傳入不同的名字即可
function sayHello(word, name){
	console.log(`${word}, ${name}!`);
}

sayHello('Welcome', 'Alice'); // 輸出 "Welcome, Alice!"
sayHello('Hello', 'Bob');   // 輸出 "Hello, Bob!"
sayHello('No', 'Charlie'); // 輸出 "No, Charlie!"
sayHello('Nice to meet you', 'David'); // 輸出 "Nice to meet you, David!"

在上面這段程式碼中,我們定義了一個名為 sayHello 的函式,它接受兩個參數:wordname。當我們呼叫這個函式時,它會輸出一個格式化的問候語。

函式回傳值

函式可以分成有回傳值和沒有回傳值兩種。

有回傳值的函式會使用 return 關鍵字來回傳一個值(結果)。

而沒有回傳值的函式則不會,只是單純的執行一些程式碼,而這類沒有回傳值的函式又稱為 void 函式。

javascript

// 這是一個會回傳兩個數字總和的函式
function add(a, b) {
	return a + b;
}
console.log(add(2, 3)); // 輸出 5



// 這是一個沒有回傳值的函式,只是輸出兩個數字的總和
function printSum(a, b) {
	console.log(a + b);
}
printSum(2, 3); // 輸出 5

TIP

可以想像成:有回傳值的函式像是自動販賣機,投入硬幣並選擇商品時,它會回傳商品

而沒有回傳值的函式則像是一個只會發出聲音的警報器,當觸發時它會發出聲音,但不會回傳任何東西。

事實上由於 JavaScript 會對所有函式進行隱式回傳處理,在 JavaScript 中沒有回傳值的函式都會回傳 undefined,這裡先不深入討論,簡單知道即可。

函式是可以組合的

函式可以被組合起來使用的,這樣可以讓程式碼更具可讀性和可維護性。

例如說我們可以定義一個函式來計算兩個數字的總和,再使用這個函式來計算其他數字的總和。

javascript
function add(a, b) {
	return a + b;
}

/**
 * 當然也可以把函式組合起來一起用
 * 先計算 1+2 = 3,再計算 3 + 7 = 10,
 * 最後再計算 3 + 7 = 10
 * 
 * 其實很像數學式 ( (1+2) + (3+4) ) = 10
 */
console.log(add(add(1, 2), add(3, 4)));


/**
 * 也可以再複雜一點
 * 計算 ( (1+2) + (3+4) ) + (5+6) = 28
 * 這樣就可以把多個函式組合起來使用
 */
console.log(add(add(add(1, 2), add(3, 4)), add(5, 6)));


/**
 * 上面的寫法可能會讓程式碼變得難以閱讀
 * 所以也可以拆成兩行來寫,先計算前兩個,再計算後兩個
 * 比較容易看懂
 */
const combined = add(add(1, 2), add(3, 4));
console.log(add(combined, add(5, 6))); // 輸出 28

箭頭函式 (Arrow Function)

前面提到了一般函式的宣告方式是 function 函式名稱(參數) { 函式內容 }

但是在 JavaScript ES6 之後,出現了一種更簡潔的函式宣告方式,稱為 箭頭函式(Arrow Function)。

寫法:const 名稱 = (參數) => { 函式內容 }

javascript
const greet = (name) => {
	return 'Hello, ' + name + '!';
};
console.log(greet('Aaron')); // 輸出 "Hello, Aaron!"


// 當函式只有一行時,可以省略大括號 `{}` 和 `return` 關鍵字
const greet = (name) => 'Hello, ' + name + '!';
console.log(greet('Aaron')); // 輸出 "Hello, Aaron!"

預設參數

前面提到函式可以給定一些參數作為輸入,但有時候會希望可以有預設的參數,當函式的參數沒有傳入值時,可以使用預設參數:

javascript
function greet(name = '訪客') {
	return 'Hello, ' + name + '!';
}

console.log(greet()); // 輸出 "Hello, 訪客!"
console.log(greet('Aaron')); // 輸出 "Hello, Aaron!"

剩餘參數

使用剩餘參數可以接收不定數量的參數,並以陣列的方式傳入到函式中:

javascript
// ...number 會將傳入的參數收集成一個陣列
function sum(...numbers) {
	let sum = 0;
	for (let i = 0; i < numbers.length; i++) {
		sum += numbers[i];
	}
	return sum;
}

console.log(sum(1, 2, 3, 4)); // 輸出 10

但要特別注意的是,剩餘參數只能作為函式的最後一個參數使用,否則會報錯。

javascript
function whatYouGot(name, ...somethings){
	console.log(`Hello, ${name}!`);
	console.log(`You got:`, somethings);
}

// 這樣會報錯,因為剩餘參數擺在前面編譯器不知道哪個是 `name` 參數
function whatYouGot(...somethings, name){
	console.log(`Hello, ${name}!`);
	console.log(`You got:`, somethings);
}

Function vs Arrow Function 差異

前面我們使用了兩種函式宣告方式:傳統函式宣告和箭頭函式。

雖然它們都可以用來定義函式,但有一些重要的差異:

傳統函式宣告可以被提升(hoisting),即使在函式宣告之前呼叫也不會報錯:

javascript
console.log(add(2, 3)); // 找得到 function:add

function add(a, b) {
	return a + b;
}

console.log(add(2, 3)); // 錯誤,因為箭頭函式不會被提升
const add = (a, b) => a + b;

TIP

提升(Hoisting)

前面提過,JavaScript 是一種直譯型語言,這意味著程式碼在執行前會被解釋器讀取並處理。

提升(hoisting)是指在 JavaScript 的編譯階段,會優先讀取 function 宣告,並將其提升到程式碼的頂部。

這意味著你可以在函式宣告之前呼叫它,而不會報錯。

像是這類會進行提升處理的有: 函式宣告(function declarations)、變數宣告(var)和類別宣告(class declarations)。

把 Function 當作變數使用

前面提到箭頭函式時,有沒有發現其實我們是宣告了一個變數並且賦予函式當作值?

對,函式本身可以被當作變數來使用。

javascript
const add = function(a, b) {
	return a + b;
};

const anotherAdd = add; // 將函式指派給另一個變數
console.log(anotherAdd(5, 10)); // 輸出 15

甚至可以將函式當作參數傳遞給其他函式:

javascript
function executeFunction(func, a, b) {
	return func(a, b);
}

const add = (a, b) => a + b;
const subtract = (a, b) => a - b;

console.log(executeFunction(add, 5, 10)); // 輸出 15
console.log(executeFunction(subtract, 10, 5)); // 輸出 5

小結

函式是程式設計中非常重要的概念,學會如何宣告、呼叫函式,以及使用參數、回傳值,能讓你的程式更具結構性與靈活性。

之後觀念更加完善之後,可以進一步探討函式的其他特性,例如作用域、閉包等。

現在試著自己動手撰寫一些函式,練習這些概念吧!

範例練習

  1. 請寫一個函式 multiply,可以回傳兩個數字的乘積。

    javascript
    // 請在這裡完成 multiply 函式
    function multiply(a, b) {
        // ...
    }
    
    console.log(multiply(3, 4)); // 預期輸出 12
  2. 請寫一個函式 greet,接收一個名字參數,並回傳 Hello, 名字! 的字串。

    javascript
    // 請在這裡完成 greet 函式
    function greet(name) {
        // ...
    }
    
    console.log(greet('Eve')); // 預期輸出 Hello, Eve!
  3. 請寫一個函式 sumAll,可以接收任意數量的數字,並回傳總和。

    javascript
    // 請在這裡完成 sumAll 函式
    function sumAll(...numbers) {
        // ...
    }
    
    console.log(sumAll(1, 2, 3, 4)); // 預期輸出 10
  4. 請將下列匿名函式改寫成箭頭函式(Arrow Function):

    javascript
    const square = function(x) {
        return x * x;
    };
    // 請改寫成箭頭函式
  5. 請寫一個函式 isAdult,接收一個年齡參數,若大於等於 18 歲回傳 true,否則回傳 false

    javascript
    // 請在這裡完成 isAdult 函式
    function isAdult(age) {
        // ...
    }
    
    console.log(isAdult(20)); // 預期輸出 true
    console.log(isAdult(15)); // 預期輸出 false

Wrirten by Aaron Su