以下內(nèi)容可能存在個人理解的部分,并非完全參照原文
如有疑問,請以書中內(nèi)容為準
接著上一篇的內(nèi)容
4.2 執(zhí)行環(huán)境及作用域
執(zhí)行環(huán)境定義了變量或函數(shù)有權(quán)訪問的其他數(shù)據(jù),決定了它們各自的行為。每一個執(zhí)行環(huán)境都有一個與之關(guān)聯(lián)的 變量對象,環(huán)境中定義的所有變量和函數(shù)都保存在這個對象中。
每個函數(shù)都有自己的 執(zhí)行環(huán)境。當執(zhí)行流進入一個函數(shù)時,函數(shù)的環(huán)境就會被推入一個環(huán)境棧中。而在函數(shù)執(zhí)行之后,棧將其環(huán)境彈出,把控制權(quán)返回給之前的執(zhí)行環(huán)境。
當代碼在一個環(huán)境中執(zhí)行時,會創(chuàng)建變量對象的一個 作用域鏈。作用域鏈的前端,始終都是當前執(zhí)行的代碼所在環(huán)境的變量對象。如果這個環(huán)境時函數(shù),則將其 活動對象 作為變量對象。作用域鏈中的下一個變量對象來自包含(外部)環(huán)境,而再下一個變量對象則來自下一個包含環(huán)境,一直延續(xù)到全局執(zhí)行環(huán)境。
簡單來說,內(nèi)部作用域里找不到就到外部作用域去找.
延長作用域鏈
有些語句可以在作用域鏈的前端臨時增加一個變量對象,該變量對象會在代碼執(zhí)行后被移除。
在兩種情況下會發(fā)生這種現(xiàn)象:
-
try-catch語句的catch塊 -
with語句
對with語句來說,會將指定的對象添加到作用域鏈中。對catch語句來說,會創(chuàng)建一個新的變量對象,其中包含的是被拋出的錯誤對象的聲明。
function buildUrl(){
var qs = "?debug=true";
with(location){
var url = href + qs;
}
return url;
}
with接收的是location對象,因此其變量對象中就包含了location對象的所有屬性和方法,而這個變量對象被添加到了作用域的前端。
MDN上的with : 不推薦使用with,在 ECMAScript 5 嚴格模式中該標簽已被禁止。推薦的替代方案是聲明一個臨時變量來承載你所需要的屬性。
沒有塊級作用域
在其他類C的語言中,由花括號封閉的代碼塊都有自己的作用域,但JavaScript并沒有塊級作用域
if(true){
var color = "blue";
}
alert(color); // "blue"
在for循環(huán)中更容易引起迷惑
for(var i=0;i<10;i++){
doSomething(i);
}
alert(i); // 10
這個在前面也有提到過
4.3 垃圾收集
JavaScript具有自動垃圾收集機制
標記清除
JavaScript中最常用的垃圾收集方式是 標記清除
當變量進入環(huán)境時,就將這個變量標記為“進入環(huán)境”。而當變量離開環(huán)境時,則將其標記為“離開環(huán)境”。
垃圾收集器在運行的時候會給存儲在內(nèi)存中的所有變量都加上標記。然后,它會去掉環(huán)境中的變量以及被環(huán)境中的變量引用的變量的標記。而在此之后再被加上標記的變量將被視為準備刪除的變量。
引用計數(shù)
另一種不太常見的垃圾收集策略叫做 引用計數(shù)
當聲明了一個變量并將一個引用類型賦給該變量時,這個值的引用次數(shù)就是1。如果同一個值又被賦給另一個變量,則該值的引用次數(shù)加1。
相反,如果包含對這個值引用的變量又取得了另外一個值,則這個值的引用次數(shù)減1。當這個值的引用次數(shù)為0時,則說明沒有辦法再訪問,可以將其占用的內(nèi)存空間回收。
但會存在一個問題: 循環(huán)引用
function problem(){
var objA = new Object();
var objB = new Object();
objA.someOtherObj = objB;
objB.anotherObj = objA;
}
當函數(shù)執(zhí)行完畢,objA和objB還將繼續(xù)存在,因為它們的引用次數(shù)永遠不會是0
管理內(nèi)存
分配給Web瀏覽器的可用內(nèi)存通常比分配給桌面應(yīng)用的少
優(yōu)化內(nèi)存最佳的方式,就是為執(zhí)行中的代碼只保存必要的數(shù)據(jù)。一旦數(shù)據(jù)不再有用,最好通過將其值設(shè)置為null來釋放其引用——這個做法叫做 解除引用
function createPerson(name) {
var localPerson = new Object();
localPerson.name = name;
return localPerson;
// localPerson在函數(shù)執(zhí)行完畢后就離開了其執(zhí)行環(huán)境
// 不需要顯示地解除引用
}
var globalPerson = createPerson("Nicholas");
// 解除引用
globalPerson = null;
解除引用不意味著自動回收該值占用的內(nèi)存,它的真正作用是讓值脫離執(zhí)行環(huán)境,以便垃圾收集器下次運行時將其回收。