一、作用域
1.1、全局作用域
(1)最外層函數和在最外層函數外的變量擁有全局作用域:
1 var i=1; 2 function a(){ 3 var aw="qw"; 4 console.log(aw); 5 function ba(){ 6 var qq="aaa"; 7 } 8 } 9 10 console.log(i); 11 condole.log(aw); 12 a(); 13 14 //1 15 //Uncaght ReferenceError : aw is not defined(...) 16 //qw
(2)所有沒有聲明變量都自動聲明擁有全局作用域:
1 function c(){ 2 o="qw"; 3 4 function ba(){ 5 p="aaa"; 6 } 7 ba(); 8 } 9 c(); 10 11 console.log(o); 12 console.log(p); 13 14 // qw 15 // aaa
o 在函數c()執行完后,擁有全局作用域,可得其值,而函數 ba()在函數執行后p 也被聲明為全局變量
(3) 一般情況下,window下所有的屬性默認擁有全局作用域,如:window.onload,window.scroll等。
二、局部作用域
與全局作用域相反,局部作用域只作用某些代碼段內,只在內部可用
1 function ew(){ 2 var e=2; 3 function q(){ 4 var s=2,w=3; 5 console.log(s); 6 var d=s+w; 7 } 8 q(); 9 console.log(d); 10 } 11 undefined 12 ew(); 13 // 2 14 // Uncaught ReferenceError: d is not defined(…)
二、作用域鏈
在JavaScript中,函數也是對象,實際上,JavaScript里一切都是對象。函數對象和其它對象一樣,擁有可以通過代碼訪問的屬性和一系列僅供JavaScript引擎訪問的內部屬性。其中一個內部屬性是[[Scope]],由ECMA-262標准第三版定義,該內部屬性包含了函數被創建的作用域中對象的集合,這個集合被稱為函數的作用域鏈,它決定了哪些數據能被函數訪問。
function fo(){ var a=3; }
這里函數文調用之前,作用域鏈如圖:
fo()
執行此函數時會創建一個稱為“運行期上下文”的內部對象,運行期上下文定義了函數執行時的環境。每個運行期上下文都有自己的作用域鏈,用於標識符解析,當運行期上下文被創建時,而它的作用域鏈初始化為當前運行函數的[[Scope]]所包含的對象。
這些值按照它們出現在函數中的順序被復制到運行期上下文的作用域鏈中。它們共同組成了一個新的對象,叫“活動對象”,該對象包含了函數的所有局部變量、命名參數、參數集合以及this,然后此對象會被推入作用域鏈的前端,當運行期上下文被銷毀,活動對象也隨之銷毀。新的作用域鏈如下圖所示:
三、代碼優化
由作用域鏈可知,全局變量通常在作用域鏈最深處,查找時間會更長,所以我們要避免調用全局表量,同時with 語句和try catch 中catch會改變作用域鏈,應該盡量避免使用with,因為try catch 語句在異常調試非常有用,因此我可以對其進行優化。