函數和作用域是JavaScript的重要組成部分,我們在使用JavaScript編寫程式的過程中經常要用到這兩部分內容,作為初學者,我經常有困惑,藉助寫此博文來鞏固下之前學習的內容。 (一)JavaScript函數 JavaScript函數是指一個特定代碼塊,可能包含多條語句,可以通過名字來供其他語 ...
函數和作用域是JavaScript的重要組成部分,我們在使用JavaScript編寫程式的過程中經常要用到這兩部分內容,作為初學者,我經常有困惑,藉助寫此博文來鞏固下之前學習的內容。
(一)JavaScript函數
JavaScript函數是指一個特定代碼塊,可能包含多條語句,可以通過名字來供其他語句調用以執行函數包含的代碼語句。
1.JavaScript創建函數的方法有兩種:
- 函數聲明:
function funcDeclaration(){ return 'A is a function'; }
- 函數表達式:
var funExpression=function(){ return 'A is a function '; }
上述函數聲明和函數表達式的區別(註明:翻譯自:不同的方式來寫一個函數)在:
JavaScript解析器中存在一種變數聲明被提升(hosting)的機制,也就是說變數(函數)的聲明會被提升到作用域的最前面,即使寫代碼的時候是寫在最後面,也還是會被提升至最前面。
例如以下代碼段:
alert(foo); // function foo() {}
alert(bar); // undefined
function foo() {}
var bar = function bar_fn() {};
alert(foo); // function foo() {}
alert(bar); // function bar_fn() {}
輸出結果分別是function foo() {}
、undefined
、function foo() {}
和function bar_fn() {}
。
可以看到foo
的聲明寫在alert之後,仍然可以被正確調用,因為JavaScript解釋器會將其提升到alert前面,而以函數表達式創建的函數bar
則不享受此待遇。
所以,JavaScript 引擎執行以上代碼的順序可能是這樣的:
- 創建變數
foo
和bar
,並將它們都賦值為undefined
。 - 創建函數
foo
的函數體,並將其賦值給變數foo
。 - 執行前面的兩個 alert。
- 創建函數
bar_fn
,並將其賦值給bar
。 - 執行後面的兩個 alert。
2.函數的參數
在調用函數時,你可以向其傳遞值,這些值被稱為參數。
function printName(name){
console.log(name);
}
printName('Byron');
printName('Casper);
其中name是形參,'Byron'和'Casper'是實參。
說到函數的參數,我們不得不提到arguments。此處涉及的內容有點多,請看客們參考樓主之前轉載的javascript arguments。
3.函數重載
重載是很多面向對象語言實現多態性的手段之一,在靜態語言中確定一個函數的手段是靠方法簽名--函數名+參數列表,也就是說相同名字的函數參數個數不同或者順序不同都被認為是不同的函數,成為函數重載。
在JavaScript中沒有函數重載的概念,函數通過名字確定唯一性,參數不同也被認為是相同的函數,後面的覆蓋前面的。
4.返回值
有時候我們希望在函數執行後給我們一個反饋,就像表達式一樣,給我們個結果,我們可以通過return
來實現
function fn(a, b){
a++;
b++;
return a + b;
}
var result = fn(2, 3);
conslole.log(result);
這樣我們就能拿到函數希望給我們的反饋了,調用return後,函數立即終端並返回結果,即使後面還有語句也不在執行。其實我們不寫return語句,函數也會預設給我們返回undefined
5.命名衝突
當在同一個作用域內定義了名字相同的變數和方法的話,無論其順序如何,變數的賦值會覆蓋方法的賦值。
var fn = 3;
function fn(){}
console.log(fn); // 3
當函數執行有命名衝突的時候,函數執行時載入順序是 變數、函數、參數
,典型例子如下:
function fn(fn){
console.log(fn);
var fn = 3;
console.log(fn);
}
fn(10); 輸出結果為:10 3
(二)函數作用域
在JavaScript中,變數的作用域有全局作用域和局部作用域兩種。
說到這裡,我們不得不提下JavaScript中的作用域鏈。
在JavaScript中,函數也是對象,實際上,JavaScript裡面一切都是對象。函數對象和其他對象一樣。
JavaScript中的函數運行在它們被定義的作用域里,而不是它們被執行的作用域里。 ----JavaScript權威指南
JavaScript的作用域的是實現和C/C++不同,並非用“堆棧”方式,而是使用列表,具體過程如下(EMA262中所述):
任何執行上下文時的作用域,都是由作用域鏈(scope chain)來實現的;
在一個函數被定義的時候,會將它定義時刻的scope chain鏈接到這個函數對象的[[scope]]屬性;
在一個函數對象被調用的時候,會創建一個活動對象(也就是一個對象),然後對於每一個函數的形參,都命名為該活動對象的命名屬性,然後將這個活動對象做為此時的作用域鏈(scope chain)最前端,並將這個函數對象的[[scope]]加入到scope chain中。
參考:
1.JavaScript 開發進階:理解 JavaScript 作用域和作用域鏈