跳至主要内容

[JS] Function基本介紹-1

TL;DR

本篇介紹Function的宣告及建立方式:

  1. 具名函式(函式陳述式)
  2. 匿名函示(函式表達式)。

參考資料

相關連結


函式簡介

在JS中,物件型別包含物件以及其子型別,例如陣列、函式。 函式與一般物件不同的地方在於:

  1. 多了被呼叫的能力,且可以傳入參數
  2. 包含了一個程式碼片段
function <function name>(參數){
程式碼片段
}

以下提供簡單的函式範例:

function fn(parameter){
var localVariable='函示fn內的區域變數';
console.log(this,localVariable);

return `回傳一段${parameter}`;
}

let data=fn('外部傳入的字串');
console.log(data) //"回傳一段外部傳入的字串"

值得注意的是,fn('外部傳入的字串')這段,因為在執行函式的時候,就會回傳(return)內容,所以本身就是一個表達式

函式包含:
  1. 可被呼叫的能力(be invoked)
  2. 程式碼片段
  3. 函式名稱(可選用)

函式陳述式

我們可以透過function保留字,來宣告一個函式。這種方式稱為函式陳述式。又因為透過這種方式所宣告的函式,一定會具備一個名字,所以又稱為具名函式。

以下提供一個簡單的函數陳述式範例:

function fnA(){
console.log('函數陳述式','具名函式');
console.log(fnA)
}

fnA();
// 函數陳述式 具名函式
// ƒ fnA(){
// console.log('函數陳述式','具名函式');
// console.log(fnA)
// }

可以看到在 Line:8 (即對應的 Line:3 )。
印出的結果為f fnA(){...}。可以看到該函式是具有名稱的。

執行結果圖示

函式陳述式執行結果

提升

在hoisting的章節也有介紹到,使用function宣告所創建的函式,在創造階段就已經包含完整的程式碼在記憶體內了。

提升的順序也會在變數之前。所以不管在程式碼中的哪個位置透過陳述式宣告函式,都可以執行。這是因為hoisting會自動將該具名函式提升到最上方。

函式表達式

另外,我們也可以透過表達式的方式來宣告一個函式。範例如下:

const fnB=function(){
console.log('函式表達式','匿名函式');
console.log(fnB);
}

fnB();
// 函式表達式 匿名函式
// ƒ (){
// console.log('函式表達式','匿名函式');
// console.log(fnB);
// }

可以看到 Line:8 印出f (){...}。該函式不具有名稱。

這是因為我們使用表達式的方式來宣告函式,我們首先先定義了一個變數(fnB),接著我們才宣告一個函式並將該函式賦予到變數上。此時我們就不用定義函式的名稱。這種方式又稱為函式表達式

執行結果圖示

函式表達式範例執行結果

提升

因為是先宣告變數,才將函式賦予到變數上,所以在hoisting的順序上也有所不同。這部分請參考hoisting的文章。

caution

需要注意,函式表達式也可以具有名稱!

所以函式陳述式等同於具名函式,但是函式表達式不一定等於匿名函式。

具名函式表達式

先前有提到,不是所有的函式表達式都是匿名函式。

範例如下:

const fnC=function fnD(){
console.log('fnC',fnC);
console.log('fnD',fnD);
}

fnC();
// fnC ƒ fnD(){
// console.log('fnC',fnC);
// console.log('fnD',fnD);
// }
// fnD ƒ fnD(){
// console.log('fnC',fnC);
// console.log('fnD',fnD);
// }

首先,我們先宣告一個變數fnC,接著使用表達式的方式,來將具名函式fnD給賦予到變數fnC上。

可以發現我們在執行fnC的時候,印出來的結果都會是fnD。這是因為fnD是這個函式的名稱!!

note

在變數fnC內存放的其實是名叫fnD的函式。

執行結果圖示

具名函式表達式範例執行結果

注意事項

danger

具名函式表達式無法在函式外被調用!

舉例來說,如果程式碼如下:

const fnC=function fnD(){
console.log('fnC',fnC);
console.log('fnD',fnD);
}

fnC(); //可以順利執行
fnD(); //無法順利執行!!! fnD沒有被定義

執行結果圖示

具名函式表達式注意事項範例執行結果

綜合範例

範例一

let num=1;
const giveMeMoney=function giveMoreMoney(coin){
num+=1;
console.log('執行GiveMeMoney',`現在的num是${num}`,`現在的coin是${coin}`);
return coin>100? coin: giveMoreMoney(num*coin);
}

console.log(giveMeMoney(30));
// 執行GiveMeMoney 現在的num是2 現在的coin是30
// 執行GiveMeMoney 現在的num是3 現在的coin是60
// 執行GiveMeMoney 現在的num是4 現在的coin是180
// 180

其實上述寫法將函式內的三元運算子撰寫為giveMeMoney(num*coin)結果也是相同的。

所以實務上使用函式表達式時可以直接省略函式名稱

執行結果圖示

範��例一執行結果

範例二

Callback function的概念,實際上與函式表達式相同,範例如下:

function callSomeone(fn){
console.log('執行callSomeone');
fn();
}

callSomeone(function(){ console.log('我是透過參數傳入的函式,等等會被執行') });
// 執行callSomeone
// 我是透過參數傳入的函式,等等會被執行

使用陳述式宣告callSomeone函式,一定需要具有名稱。但是當我們在傳入參數(fn)的時候,就如同表達式的方式,在執行callSomeone函式時,創造一個執行環境並且創建一個fn的變數提供外部參數傳入,只是這邊的參數是傳入一個函式。

此時,我們外部所傳入的參數就不需要給予名稱,如同表達式的方式賦予到fn變數上,並且接著馬上在 Line:3 執行。

執行結果圖示

範例二執行結果