1. 程式人生 > >JavaScript深入理解函式引數列表及“不存在過載”

JavaScript深入理解函式引數列表及“不存在過載”

函式的引數

JS和其他大多數語言一個較為明顯的區別就在於函式引數的處理上。因為在JS中呼叫函式的時候,傳入的引數資料型別是可以不固定的,個數也無所謂多少個。聽起來很奇怪,實際上,JS中的引數在內部是用一個數組表示的,不論傳入的引數屬於什麼資料型別,有多少個,函式接收的都是這個陣列。解析器並不對陣列內容進行檢查。(在JS中,陣列的長度是可變的,儲存元素的型別也是不固定的。)

我們知道了函式引數儲存在一個數組中,那麼我們如何去訪問這個陣列呢?————通過argument物件。argument物件是函式內部的一個物件,它儲存著傳入函式的所有引數。類似於陣列,我們可以通過argument[0],argument[1]這樣的方式去訪問傳入的引數。(但argument並不是Array的例項!)argument擁有一個名為length的屬性,該屬性的值表示實際傳入引數的個數。

我們來看一個例子:

function info(name, age) {
  alert("hi " + name + ", you are " + age + " years old.");
}   

我們定義上面的函式,然後通過如下方式呼叫:

info("Tom", 23);

結果會輸出:

hi Tom, you are 23 years old.

以上的過程都理所當然,正如我們在Java,c/c++中一樣。我們這樣去呼叫info()函式當然沒有問題,但是可能並沒有理解JS中函式真正的特性。事實上,我們也可以用以下的方式來呼叫函式:

info("Tom");

得到以下輸出:

我們只傳入了一個引數,因此在訪問第二個引數age的時候得到了undefined值,我們在訪問陣列未賦值的元素時也會返回undefined值。不論我們實際傳入函式的引數是什麼資料型別,有多少個引數,函式接收到的永遠是那個通過arguments物件訪問的陣列。當我們只傳入了一個引數時,訪問第二個引數就得到了undefined值。

實際上,上面的info()方法我們完全可以這樣寫:

function info() {
  alert("hi " + arguments[0] + ", you are " + arguments[1] + " years old.");
}   

當我們用info(“Tom”, 23)來呼叫這個函式的時候,同樣會得到:

hi Tom, you are 23 years old.

如果我們看得深入一點,會發現:我們在函式定義時,在圓括號中寫入的命名引數,只不過是為了方便閱讀,方便呼叫時更好地傳入合適的引數,方便在函式內部更便利地使用這些引數罷了。實際上,寫不寫這些命名引數都不會影響函式的功能。

我們甚至可以定義這樣的函式:

function math(abc, def, ghi) {
  if(arguments.length == 1) {
    return arguments[0];
  }
  if(arguments.length == 2) {
    return arguments[0] + arguments[1];
  }
}

於是在我們傳入一個引數的時候返回該引數本身,傳入兩個引數的時候返回這兩個引數的和。至於定義函式時引數列表是什麼,根本就無所謂。

上面定義的function math(abc, def, ghi)函式,根據傳入引數個數的不同執行不同的操作,有點類似於函式過載的意思了。然而JS中函式並不存在過載!為什麼?

為什麼JS沒有過載

理解JS中為什麼不存在函式過載,首先要搞清楚函式過載的概念:

函式過載是指在同一作用域內,可以有一組具有相同函式名,不同引數列表的函式,這組函式被稱為過載函式。過載函式通常用來命名一組功能相似的函式,這樣做減少了函式名的數量,避免了名字空間的汙染,對於程式的可讀性有很大的好處。

我們上面說過,JS中引數列表對函式的功能是沒有絲毫影響的!引數列表只是為了我們更方便地使用函式而設定的,真正呼叫函式時,起作用的永遠是一個存放參數的陣列。僅憑這一點,指望通過不同的形參列表實現函式過載就是不可能做到的。

另外,在JS中,函式實際上是Function型別的例項。函式是物件,函式名實際上就是指向函式物件的指標。所以我們在定義同名函式的時候,後定義的函式只不過是覆蓋了先定義函式的引用變數罷了。

例如:

  function fun(arg1) {
    return arg1;
  }

  function fun(arg1, arg2) {
    return arg1 + arg2;
  }

本質上和以下程式碼是一樣的:

  function fun(arg1) {
    return arg1;
  }

  fun = function() {
    return arguments[0] + arguments[1];
  }

fun變數首先指向首先定義的函式

  function fun(arg1) {
    return arg1;
  }

然而在建立第二個函式的時候,該變數被覆蓋了,重新指向新定義的函式。因此過載在JS中是不可能存在的。