iT邦幫忙

2023 iThome 鐵人賽

DAY 16
2
Modern Web

GPT 救救我!菜雞小海獺的前端成長之旅系列 第 16

D16 - JS - 基礎 - 函式的宣告與名詞定義

  • 分享至 

  • xImage
  •  

函式是 JS 的基本。為了讓 JS 幫我們執行一些重複的任務或計算,我們需要把這些程式碼寫成一個函式,依據輸入值的不同,得到不同的結果,進而達到重用的目的。
使用函式前,得先宣告函式,才能呼叫(call)它。

宣告

函式陳述式(Function statements)/函式宣告(Function declaration)

一般函式宣告,長相大概是這樣:

function name(arguments){
	// ...
	return sth;
}
  • 使用 function 為開頭來宣告
  • 後接函式名稱(name 的位置);名稱可有可無(沒名稱的話 → 匿名函式)。
  • 小括號()內可能有零至多個藉由逗號分隔的參數(arguments)
  • 大括號{}內為計算的過程、也可以是這個函式要執行的任務
  • 藉 return 回傳大括號內的結果
  • 沒有使用 return 的話,預設回傳的值為 undefined
  • 若有需求,可遞迴使用。(在函式內呼叫自己)
  • 會提升(hoisting)自己

所以今天若要寫一個函式讓它幫我算 x+y,可以這樣寫:

function add(x, y) {
  let z = x + y;
  return z;
}

console.log(add(3, 4)); // 7
console.log(add(5, 6)); // 11

// 函式的提升
console.log(plus(4, 5)); // 9;雖然呼叫早於宣告,但仍成功呼叫

function plus(x, y) {
  let z = x + y;
  return z;
}

說明:

使用 add(3,4) 來呼叫,沒有呼叫的話,函式不會執行,前面寫的 function add(x,y) 僅是定義而已。
函式可以重複使用,以範例來說,輸入不同的 x & y 可得到不同結果。

備註:

  • 若呼叫先於函式的宣告,函式不會執行。
  • 函式與參數可依需求自由設定,參數不只可放數字或字串,你也可以丟一包 object 進去。
  • 函式內部 { } 宣告的任何變數,無法被外部讀取。

函式表達式(Function Expressions)

同樣的例子也可以寫成函式表達式。它可以是匿名的(沒有函式名稱)、也可以是有函式名稱的。有函式名稱時,可以在函式內代指自己(遞迴,Recursion)。但使用遞迴時,要小心無限迴圈產生過度堆疊(Stack Overflow)的問題;要避免此種情況,需要設定對應條件以避免無限循環。我就這樣第一次把瀏覽器玩壞了XD

常見情況:

  • 通常會將匿名函數與事件處理程序一起用。
  • 將函式做為參數傳給另一個函式使用
// 修改原本範例
let add = function (x, y) {
  let z = x + y;
  return z;
};

add(3, 4); // 7
add(5, 6); // 11
add(-1, 3); // 2

// 匿名+事件處理,MDN 範例
var myButton = document.querySelector("button");

myButton.onclick = function () {
  alert("hello");
};

// 將函式做為參數傳遞給另一函式使用,MDN 範例
function map(f, a) {
  const result = new Array(a.length);
  for (let i = 0; i < a.length; i++) {
    result[i] = f(a[i]);
  }
  return result;
}

// 遞迴,借用 MDN 範例
function factorial(n) {
  if (n == 0 || n == 1) return 1;
  else return n * factorial(n - 1);
}

以建構式 new Function 建立

  • 使用 new Function 創建的函式不會創建當前環境的閉包(closures)
  • 這些函式只能在全域中執行,所以只能讀到全域變數跟自己的區域變數
  • 無法訪問到創建自己的函式的作用域內的變數
  • 運作效能較差

……但我沒用過,說明到此已經是極限了 ((;゚Д゚)

ps. 官方想表達作用域不同的範例 code 無法正常執行,就不放了。


作用域(Function scope)

  • 函式內定義的變數無法被外部訪問
  • 但函式可訪問在其作用域 { } 內定義的所有變數與函式

由於這樣的特性,在全域宣告的函式可訪問在全域中的所有變數跟函式;在函式內宣告的子函式,可以訪問父函式內的所有變數與函式、父函式也可訪問所有子函式內的變數與函式。

const x = 100;
let add = function (y) {
  let z = x + y;
  return z;
};

console.log(add(4)); // 104
console.log(x); // 100
console.log(z); // 讀不到 fn 內的 z;ReferenceError: z is not defined

閉包(closures)

利用作用域的特性限制變數或函式的存取。
閉包的坑會另開一篇文章來填 ˊ w ˋ

預設參數(Default parameters,ES2015/ES6)

  • 某些情況下可以為參數設定預設值。
  • 可允許傳入的參數沒有值/undefined,但又不會讓函式壞掉。
  • 以前沒有此功能時,都需在函式內寫檢查用的 code (現在不用寫了,耶)
// MDN 的範例
function multiply(a, b = 1) {
  return a * b;
}

multiply(5, 2); // 10
multiply(5, 1); // 5
multiply(5); // 5

其餘參數(rest parameter)

// 語法,來自 MDN
function f(a, b, ...theArgs) {
  // ...
}
  • ...theArgs 會收集剩下的參數(也就是沒被指定名稱的參數),並把這些參數們當作實體陣列。
  • 所以可對 ...theArgs 使用陣列的各種方法。

以上面的語法示意來說,若傳入了 a, b, c, d, e, f, ... 等參數,c 以後的參數會被 ...theArgs 收容,也可以解開這個陣列(解構賦值,Destructuring assignment)。關於解構賦值,之後會開文章來填坑。

// 範例來自 MDN
function f(...[a, b, c]) {
  return a + b + c;
}

f(1); // NaN (b 和 c 都是 undefined)
f(1, 2, 3); // 6
f(1, 2, 3, 4); // 6 (第四個參數不會被解構,因為解構式只有三個定義好的變數名稱)

回呼函式(Callback function)

簡單說,就是把一個函式當參數傳進另一個函式中。

// code from MDN
function greeting(name) {
  alert("Hello " + name);
}

function processUserInput(callback) {
  let name = prompt("輸入你的名字:");
  callback(name);
}

processUserInput(greeting);

補充:arguments(引數) & parameters(參數)

宣告時,小括號 () 內的都叫參數;呼叫時,填進小括號內的都叫引數

function foo(參數) {
	// ...
}

foo(引數);

呼,終於寫完了,差點越寫越多寫不完 (☉д⊙)
接下來是陣列與物件的操作~~


上一篇
D15 - JS - 基礎 - 資料型別、運算子、陳述式與表達式定義
下一篇
D17 - JS - 基礎 - 陣列 - 1
系列文
GPT 救救我!菜雞小海獺的前端成長之旅30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言