”JavaScript中的函數(shù)運(yùn)行在它們被定義的作用域里,而不是它們被執(zhí)行的作用域里.”
全局作用域(Global Scope)
最外層函數(shù)和變量
在代碼中任何地方都能訪問到的對象擁有全局作用域
window對象的屬性擁有全局作用域
vara=10;// 全局
(function(){
???? var b=20;// 函數(shù)
})();
console.log(a);// 10
console.log(b);// error, b in not defined
eval("var a = 1;");// eval
作用域鏈(Scope Chain)
在 JavaScript 中沼死,函數(shù)也是對象,實(shí)際上崔赌,JavaScript里一切都是對象
作用域的實(shí)現(xiàn)卻和C/C++不同意蛀,并非用“堆棧”方式健芭,而是使用列表县钥,具體過程如下(ECMA262中所述):
任何執(zhí)行上下文時(shí)刻的作用域, 都是由作用域鏈(scope chain, 后面介紹)來實(shí)現(xiàn).
在一個(gè)函數(shù)被定義的時(shí)候, 會(huì)將它定義時(shí)刻的scope chain鏈接到這個(gè)函數(shù)對象的[[scope]]屬性.
在一個(gè)函數(shù)對象被調(diào)用的時(shí)候,會(huì)創(chuàng)建一個(gè)活動(dòng)對象(也就是一個(gè)對象), 然后對于每一個(gè)函數(shù)的形參慈迈,都命名為該活動(dòng)對象的命名屬性, 然后將這個(gè)活動(dòng)對象做為此時(shí)的作用域鏈(scope chain)最前端, 并將這個(gè)函數(shù)對象的[[scope]]加入到scope chain中.
eg: 在調(diào)用func的時(shí)候, 會(huì)創(chuàng)建一個(gè)活動(dòng)對象(假設(shè)為aObj, 由JS引擎預(yù)編譯時(shí)刻創(chuàng)建, 后面會(huì)介紹)若贮,并創(chuàng)建arguments屬性,
然后會(huì)給這個(gè)對象添加倆個(gè)命名屬性aObj.lps, aObj.rps; 對于每一個(gè)在這個(gè)函數(shù)中申明的局部變量和函數(shù)定義,
都作為該活動(dòng)對象的同名命名屬性.
然后將調(diào)用參數(shù)賦值給形參數(shù),對于缺少的調(diào)用參數(shù)痒留,賦值為undefined谴麦。
var name = 'laruence';
function echo() {????
?alert(name);?????
var name = 'eve';????
?alert(name);?????
alert(age);}?
echo();
undefined//alert定義時(shí)的作用域鏈+alert自己參數(shù)+函數(shù)內(nèi)均無name
eve
[腳本出錯(cuò)]
延長作用域鏈
有些語句可以在作用域鏈的前端臨時(shí)增加一個(gè)變量對象,該變量對象會(huì)在代碼執(zhí)行后被移除伸头。有兩種情況下會(huì)發(fā)生這種現(xiàn)象匾效。
try-catch語句中的catch塊
with語句
變量初始化階段
JS解釋器如何找到我們定義的函數(shù)和變量?
變量對象 (Variable Object, 縮寫為VO) 是一個(gè)抽象概念中的“對象”熊锭,它用于存儲(chǔ)執(zhí)行上下文中的:
變量
函數(shù)聲明
函數(shù)參數(shù)
VO按照如下順序填充:
函數(shù)參數(shù) (若未傳入弧轧,初始化該參數(shù)值為undefined)
函數(shù)聲明 (若發(fā)生命名沖突,會(huì)覆蓋)
變量聲明 (初始化變量值為 undefined碗殷,若發(fā)生命名沖突精绎,會(huì)忽略。)