函式運算式

函式運算式是指在需要運算式時建立的函式。您通常會將函式運算式視為指派給變數的值。雖然函式宣告一律需要名稱,但您可以透過函式運算式省略 ID,並在 function 關鍵字後方加上包含選用參數的括號,藉此建立匿名函式:

const myVariable = function() { };

接著,您可以使用變數的 ID 呼叫這些函式運算式:

const myVariable = function() {
    console.log( "This is my function." );
};

myVariable();
> "This is my function."

您也可以使用函式運算式來建立已命名函式,其語法與函式宣告類似:

const myVariable = function myFunction() {
    console.log( "This is my function." );
};

myVariable();
> "This is my function."

但是,與函式宣告不同的是,已命名函式運算式只能由函式內部透過函式名稱存取:

const myVariable = function myFunction() {
  console.log( `I'm a ${ typeof myFunction }.`);
};

typeof myFunction;
> "undefined"

typeof myVariable;
> "function"

myVariable();
> "I'm a function."

與函式運算式相關聯的名稱主要用於偵錯。已命名的函式運算式也可以遞迴方式呼叫本身,但這在現代開發中並不常見:

const myVariable = function myFunction() {
    console.log( "One second elapsed." );
    setTimeout( myFunction, 1000 );
};

setTimeout( myVariable, 1000 );
> "One second elapsed."
> "One second elapsed."
> "One second elapsed."
…

箭頭函式運算式

在 ES6 中導入了箭頭函式運算式 (通常稱為「箭頭函式」,或很少稱為「lambda 函式」),目的是透過精簡的語法,以一些獨特的行為建立匿名函式運算式。

您可以在應使用運算式的任何位置建立箭頭函式,例如為指派給變數的值。以最常見的形式,箭頭函式是由一組相符的括號 (包含零個或多個參數)、由一個等號和大於字元 (=>) 組成的箭頭,以及一組相符的大括號,當中包含函式主體:

const myFunction = () => {};

在某些情況下,您可以將語法進一步縮小。如果您只使用一個參數,可以省略開頭的括號:

const myFunction = myParameter => {};

如果您希望函式主體傳回單一運算式的值,則不得使用大括號括住函式主體,也不必使用 return 關鍵字

const myFunction = () => 2 + 2

myFunction()
> 4

箭頭函式不會重複,因此沒有專屬的 argumentsthis 值背景資訊。相反地,他們會繼承箭頭函式詞形外圍環境 (提供這些背景資訊的最近封閉環境) 中的兩個值。

function myParentFunction() {
    this.myProperty = true;
    let myFunction = () => {
            console.log( this );
    }
    myFunction();
};

let myInstance = new myParentFunction();
> Object { myProperty: true }

呼叫箭頭函式

箭頭函式繫結引數的方式與其他類型的函式不同。箭頭函式主體中的 arguments 物件會從該箭頭函式最接近的詞條外包環境繼承值:

function myFunction() {
    let myArrowFunction = () => {
            console.log( arguments[ 0 ] );
    }
    myArrowFunction( true );
};

myFunction( false );
> false

在這個範例中,使用引數 false 呼叫的外部函式會使用引數 true 呼叫內部箭頭函式。由於箭頭函式內的 arguments 物件會解析為外部函式中的繫結,因此內部函式會記錄外部函式的 false

如果沒有可沿用父項結構定義的 arguments 物件,表示未定義箭頭函式的 arguments 物件,而嘗試存取該物件會導致錯誤:

let myArrowFunction = () => {
    console.log(arguments);
};
myArrowFunction( true );
> Uncaught ReferenceError: arguments is not defined

立即叫用的函式運算式 (IIFE)

立即叫用的函式運算式 (IIFE) 有時也稱為「自行執行匿名函式」,是在定義時立即呼叫的函式運算式。IIFE 會使用將函式納入群組運算子中建立的函式運算式。第二組相符的括號接著呼叫函式,位置立即是在函式定義本身後方,或緊接在分組運算子之後。如果使用標準函式,這兩種方法之間不會有實質差異:

(function() {
    console.log( "IIFE.")
    }
)();
> "IIFE."

(function() {
    console.log( "IIFE.")
    }
());
> "IIFE."

第一個範例呼叫分組函式運算式。第二個範例呼叫分組運算子中的函式宣告,然後評估最終結果為分組的運算式。兩者的結果相同。

不過,IIFE 是箭頭函式時會有一些差異。在此情況下,用於呼叫函式的括號必須位於分組運算子外,因為其本身的箭頭函式本身並不是運算式,而是必須在預期運算式的結構定義中建立。如果您嘗試從分組運算子的範圍中呼叫箭頭函式,代表在運算式的結構定義中呼叫尚未建立的箭頭函式:

( () => {
    console.log( "IIFE." );
}() );
> Uncaught SyntaxError: missing ) in parenthetical

由於分組運算子需要有運算式,因此系統會定義其中的箭頭函式,讓其後方的括號呼叫分組的運算式:

( () => {
    console.log( "IIFE." );
} )();
> "IIFE."

舊版應用程式經常使用 IIFE 管理範圍,尤其為了避免使用函式範圍變數函式宣告汙染全域範圍。在 ES6 中引入區塊範圍之前,常見的做法是將整個指令碼納入 IIFE,以防止全域範圍意外汙染。

隨堂測驗

您是否可以在函式外使用名稱呼叫已命名函式運算式?