1. 程式人生 > >簡析JavaScript中的Function型別(一)——函式名是指標

簡析JavaScript中的Function型別(一)——函式名是指標

浪費了“黃金五年”的Java程式設計師,還有救嗎? >>>   

說起來ECMAScript中什麼最有意思,用原書(《JavaScript高階程式設計》)作者的話說——莫過於函數了,有意思的根源在於函式實際上是物件。每個函式都是Function型別的例項,而且都與其他引用型別一樣具有屬性和方法。由於函式是物件,因此函式名實際上也就是一個指向函式物件的指標,不會與某個函式繫結。

函式的定義方式有三種:

  1. 函式宣告
  2. 函式表示式
  3. 使用Function建構函式

函式宣告的方式是比較常見的一種,如下面例子所示:

function sum(num1, num2){
  return num1 + num2;
}

這與下面使用函式表示式定義函式的方式幾乎相差無幾:

var sum = function(num1, num2){
  return num1 + num2;
};

上面的函式表示式語法定義了變數sum,並將其初始化為一個函式。有讀者可能會注意到,function關鍵字後面沒有函式名,這是因為在使用函式表示式定義函式的時候,沒有必要使用函式名,通過變數sum即可以引用函式。另外,還要注意函式未尾有個分號,就像宣告其它變數一樣。

使用Function建構函式定義函式時,Function建構函式可以接收任意數量的引數,但最後一個引數始終都被看作是函式體,而前面的引數則枚舉出了新函式的引數。來看下面的例子:

var sum = new Function('num1', 'num2', 'return num1 + num2');

從技術上講,這也是一個函式表示式。但是,我們不推薦使用這種方式定義函式,因為這種函式定義方式會導致解析兩次程式碼(第一次是解析常規ECMAScript程式碼,第二次是解析傳入建構函式中的字串),從而影響效能。不過,這種方式對於理解“函式是物件,函式名是指標”來說倒是非常直觀的。

由於函式名僅僅是指向函式的指標,因此函式名與包含物件指標的其他變數沒有什麼不同。換句話說,一個函式可能會有多個名字,如下例所示:

function sum(num1, num2){
  return num1 + num2;
}
console.log(sum(10, 10));// 20

var anotherSum = sum;
console.log(anotherSum(10, 10));// 20

sum = null;
console.log(anotherSum(10, 10));// 20

上面的程式碼首先定義了一個名為sum的函式,用於求兩個數的和。然後,又聲明瞭變數anotherSum,賦值為sum,此時anotherSumsum就指向了同一個函式,因此anotherSum()也正常返回了結果。即使切斷sum與函式物件的引用關係,也不會影響anotherSum

函式名作為指標,也可以理解為什麼ECMAScript中沒有函式過載(函式名相同,引數列表不同)的概念。來看下面的示例:

function add(num){
  return 100 + num;
}
function add(num, num2){
  return 200 + num;
}
console.log(add(100));// 300

按照函式過載的概念,兩個add函式的引數列表不同,當傳入一個引數時應該呼叫第一個add,當傳入兩個引數時應該呼叫第二個add。但如上例所示,即使傳入一個引數依然是呼叫第二個add,結果為300,這是為什麼呢?

答案在函式名是指標,所以第二個add覆蓋了第一個add,也許用函式表示式的寫法更容易理解:

var add = function(num){
  return 100 + num;
};
var add = function(num, num2){
  return 200 + num;
};

如上例所示,重新宣告add會覆蓋第一個add。而JavaScript中的函式呼叫會根據實際傳入的引數個數按順序匹配引數列表,比如這裡傳入了一個100,那麼add中的num就為100num2undefined,如果像這樣呼叫add(100, 20),那麼num2則為20

ECMAScript 2015引入了let關鍵字可以避免變數覆蓋的問題,如下所示:

var add = function(num){
  return 100 + num;
};
//Uncaught SyntaxError: Identifier 'add' has already been declared
let add = function(num, num2){
  return 200 + num;
};

解析的時候已經報錯,提示add已經宣告,所以使用let