最近在工作中發現了一個以前沒有注意到的問題,找到了解決方法及解釋,所以特在此做了一個紀錄。
在Java中,很多人都應該有同名函式,不同函式簽名的多載Overload概念,但是在JavaScript中,是不能把這樣的觀念一併套用的。
我們來看一下以下的程式碼:
<html>
<body>
<script type="text/javascript">
function foo(a) {
alert('foo(a) is called!');
}
function foo(a,b,c) {
alert('foo(a,b,c) is called!');
}
foo('a');
</script>
</body>
</html>
如果有寫過Java但不熟JavaScript的話,很容易就會想說跳出的視窗應該是 "foo(a) is called!" 的吧,不過事實上並不是這樣的。
事實上跳出的事窗會是 "foo(a,b,c) is called!",也就是說 foo(a) 這個function跟本就沒被呼叫到,很顯然的,JavaScript並不會因為不同的方法簽名呼叫就使用不同方法簽名的方法,也就是沒有Java常見的多載(overload)特性。
其實在JavaScript中,函式的名稱只是一個類似指標(C語言)或參考(Java的reference)的查西,在每一次指定function內容及名稱時,名稱指向的位置都會改變,上述的例子中,第一次foo指向一個function及記憶體位置,而第二次又指向了另外一個function內容及記憶體位置,所以foo(a)就沒用了,最後foo代表的就是foo(a,b,c)。
在JavaScript中,呼叫方法function)時,輸入的參數是以類似陣列的方式傳進方法中的,所以其實方法簽明中的輸入參數個數並不是很重要,甚至可以傳入參數給方法簽名沒有輸入參數的方法(當然在方法簽名上標名變數名稱有利於程式易讀性及使用)。
如以下的程式碼是完全可以成功且正確的執行的:
<html>
<body>
<script type="text/javascript">
function sum() {
var inputArguments = sum.arguments;
var answer = 0;
for (i=0 ; i < inputArguments.length ; i++){
answer = answer + inputArguments[i];
}
alert('輸入了' + inputArguments.length + '個數字,' + '總合為:' + answer);
}
sum(1);
sum(1,2);
sum(1,2,3);
</script>
</body>
</html>
執行後將可以看到三個視窗跳出來,分別顯示如下文字:
輸入了1個數字,總合為:1
輸入了2個數字,總合為:3
輸入了3個數字,總合為:6
可以發現,在JavaScript中,方法本身其實就像是一個物件,例如在上例中 sum() 本身就像是一個物件,可以用sum.arguments來存取輸入的參數,而輸入的參數也是像一個物件,可以存取其length及各元素的值。
而也因為 sum 就代表名為 sum 的方法,所以如果我們用方法的名稱去宣告一個新變數是會發生問題的,例如像下面這個例子,就會發生
TypeError: Cannot read property 'arguments' of undefined 的錯誤 ,因為此時sum()代表呼叫sum函式,而使用了 var sum=0 時,sum.arguments.length的sum變成指向一個sum()中的sum屬性了,所以是還沒defined的(這時真正執行的順序是var sum; --> alert(sum.arguments.length); --> alert(sum); 請參考
"Javascript的變量聲明提昇"):
<html>
<body>
<script type="text/javascript">
function sum() {
try{
alert(sum.arguments.length); //這裡的sum是指sum()中的sum屬性,但因沒有定義而會出錯
var sum = 0;
alert(sum);
}catch(error){
alert(error);
}
}
sum(1,2); //加括號時,sum指的是名為sum的function
</script>
</body>
</html>
那正確的寫法是什麼呢?事實上argument這個屬性是可以直接在function裡使用而不用在前面特別指定function名稱的,所以這樣寫就不會報錯了:
<html>
<body>
<script type="text/javascript">
function sum() {
try{
alert(arguments.length);
var sum = 0;
alert(sum);
}catch(error){
alert(error);
}
}
sum(1,2);
//輸出為:
//2
//0
</script>
</body>
</html>
如果這樣的話,也就是沒有下var sum=0,即沒有跟sum()的名稱衝突的話,也是可以的,此時sum就是function了
<html>
<body>
<script type="text/javascript">
function sum() {
try{
alert(arguments.length);
var sum2 = 0;
alert(sum);
}catch(error){
alert(error);
}
}
sum(1,2);
//輸出為:
//2
//function sum() {
// try{
// alert(arguments.length);
// //alert(typeof(sum));
// var sum2 = 0;
// alert(sum);
// }catch(error){
// alert(error);
// }
//}
</script>
</body>
</html>
參考資料:
- Javascript同名函式
- The arguments array- the secret to robust functions