定義JS函數與函數聲明、Function構造函數、函數表達式
定義一個JavaScript函數有多種方式,其中function函數聲明、Function構造函數創建、函數表達式是3種比較常用的方式,ECMAScript 6標準中又規定了幾種新的函數定義方式。在JavaScript中每個函數都是一個Function對象,它不僅能像對象一樣擁有屬性和方法,而且可以被調用。
1. 函數的定義
1.1 三種函數定義方式
定義一個函數有多種方法:
函數聲明 (函數語句)
使用函數聲明(函數語名句)定義一個函數語法如下:
function name([param,[, param,[..., param]]]) { statements }
name - 函數名
param - 傳遞給函數的參數,最多有255個
Advertisements
statements - 函數體
函數表達式
函數表達式和函數聲明非常類似,不同的是函數表達式可以省略函數名,從而定義一個匿名函數。
函數表達式語法結構如下:
function [name]([param1[, param2[, ..., paramN]]]) { statements }
name - 函數名。可以省略,省略時即為匿名函數。
param - 傳遞給函數的參數,最多有255個
statements - 函數體
Function構造函數
也可以使用new操作符通過Function構造函數創建:
new Function ([arg1[, arg2[, ...argN]],] functionBody)
Advertisements
arg1, arg2, ... argN - 函數使用的參數
functionBody - 一個表示函數定義的JavaScript語句的字元串。
1.2 ES6中新增的函數定義方式
在ES 6(ECMAScript 2015)中新增了「Generator函數」(生成器函數),這種函數同樣有三種定義方式:
生成器函數聲明 - function* 語句
類似與函數聲名,只是需要在function關鍵字後面增加一個"*"號:
function* name([param[, param[, ...param]]]) {statements }
生成器函數表達式 - function* 表達式
同樣的,生成器函數也可以使用表達式定義的方式:
function* [name]([param] [, param] [..., param]) { statements }
生成器函數構造函數 - GeneratorFunction
new GeneratorFunction (arg1, arg2, ... argN, functionBody)
箭頭函數-"=>"
除了生成器函數外,ES6中還新增了一種更簡短的函數定義方式-箭頭函數:
([param] [, param]) => { statements } param => expression
param - 函數參數
statements 或 expression - 聲明多個語句時需要用大括弧括起來,而單個表達式則不用。
2. 函數聲明、Function構造函數、函數表達式的比較
函數聲明、Function構造函數、函數表達式幾種函數定義示例如下:
構造函數使用字元串做為函數體,這會阻止JS引擎的語法檢查及優化等。因此,不推薦使用這種定義方式。而函數聲明與函數表達式非常類似,但還是存在一些差別。這三種方式主要有以下幾個方面的差別:
函數變數可以再賦值
使用函數聲明定義的函數名不能被改變,而使用函數表達式定義的函數變數可以再被賦值。當函數表達式使用函數名時,這個名稱只能在函數體內使用,在其它位置使用將會報錯:
var y = function x() {};
alert(x); // throws an error
而'new Function'定義時沒有函數名,所以也不能在其內部訪問。
函數聲名會帶來作用域的提升
函數聲名會帶來作用域的提升,所在可以在函數聲名前使用函數聲名中的函數名:
函數表達式會創建一個閉包,它定義的函數繼承了當前的作用域,並不會有作用域的提升:
而Function構造函數不會繼承全局作用哉以外的任何作用域。
Function構造函數定義的函數會被解析多次
通過函數表達式定義的函數和通過函數聲明定義的函數只會被解析一次。Function構造函數定義的函數確不同,構造函數被調用一次,其中的函數體字元串都要被解析一次。
雖然函數表達式每次都創建了一個閉包,但函數體不會被重複解析,因此仍然比構造函數定義的函數要快。