紅寶書《JavaScript高級程序設計》學習筆記(四)變量、作用域和內存問題

4.1基本類型和引用類型的值

ECMAScript變量可能包含兩種不同數據類型的值:基本類型引用類型值。基本類型值指的是簡單的數據段,而引用類型值指的是那些可能由多個值構成的對象。

5種基本數據類型(Undefined、Null、Boolean、Number、String)是按值訪問的,因爲可以操作保存在變量中的實際的值。

操作對象時,複製保存對象的某個變量時,操作的時對象的引用,在爲對象添加屬性時,操作的時實際的對象。

當一個變量從另一個變量複製引用類型的值時,實際上這個值的副本是指針,指向存儲在堆中的一個對象。複製結束後,兩個變量實際上將引用同一個對象,因此只要改變其中一個變量,就會影響另一個變量。

var obj1 = new Object();
var obj2 = obj1;
obj1.name = 'Tom';
alert(obj2.name);

ECMAScript所有函數都是按值傳遞的。也就是說,把函數外部的值複製給函數內部的參數,就和把值從一個變量複製到另一個變量一樣。(參數實際上是函數的局部變量)

函數參數中,對象是按值傳遞的。

爲了證明這件事,先來看一段代碼。

function setName(obj){
    obj.name = 'Tom';
    obj = new Object();
    obj.name = 'Black';
}

var person = new Object();
setName(person);
alert(person.name);//"Tom"

在把person傳遞給setName()後,其name屬性被設置爲“Tom”,然後又將一個新對象賦給obj,同時將name屬性設置爲“Black”。如果person是按照引用傳遞的,那麼person就會自動被修改爲指向name的屬性值“Black”的新對象,但是顯示的值爲“Tom”。說明即使在函數內部修改了參數的值,但原始的引用仍然保持未變。實際上,當在函數內部重寫obj時,這個變量引用的就是一個局部對象了,而這個局部對象會在函數執行完畢後立即銷燬。

 

檢測變量是什麼基本類型使用typeof,但是對於null來說,返回Object

let n = null;
alert(typeof null);//"Object"

檢測變量是什麼類型的對象使用instanceof

alert(person instanceof Object);//變量person是Object嗎
alert(colors instanceof Array);//變量colors是Array嗎
alert(pattern instanceof ReExp);//變量pattern是RegExp嗎

如果使用instanceof檢測基本類型的值,會始終返回true,因爲基本類型不是對象。

ECMA-262規定任何在內部實現[[Call]]方法的對象都應該在應用typeof操作符時返回“function”。

4.2執行環境及作用域 

執行環境(execution context)是JavaScript中最爲重要的一個概念,執行環境定義了變量或函數有權訪問其他數據,決定了它們各自的行爲。每個執行環境都有一個與只關聯的變量對象(variable object),環境中定義的所有變量和函數都保存在這個對象中。雖然我們編寫的代碼無法訪問這個對象,但解析器在處理數據時會在後臺使用它。

全局執行環境是最外圍的一個執行環境。根據ECMAScript實現所在宿主環境不同,表示執行環境的對象也不一樣。在Web瀏覽器中,全局執行環境被認爲是window對象,爲此所有全局變量和函數都是座位window對象的屬性和方法創建的。某個執行環境中的所有代碼執行完畢後,該環境被銷燬,保存在其中的所有變量和函數定義也隨之銷燬(全局執行環境直到應用程序退出——例如關閉網頁或瀏覽器——時才銷燬)。

每個函數都有自己的執行環境。當執行流進入一個函數時,函數的環境就會被推入一個環境棧中。而在函數執行完畢後,棧將其環境彈出,把控制權返回給之前的執行環境。ECMAScript程序中的執行流正是由這個方便的機制控制者。

當代碼在一個環境中執行時,會創建變量對象的一個作用域鏈(scope chain)。作用域鏈的用途,是保證對執行環境有權訪問的所有變量和函數的有序訪問。作用域鏈的前端,始終都是當前執行的代碼所在環境的變量對象。如果這個環境是函數,則將其活動對象(activation object)作爲變量對象。活動對象是在最開始只包含一個變量,即arguments對象(這個對象在全局環境中是不存在的)。作用域鏈中的下一個變量對象來自包含(外部)環境,而再下一個變量對象則來自下一個包含環境。這樣一直延續到全局執行環境;全局執行環境的變量對象始終都是作用域鏈中的最後一個對象。

標識符解析試試沿着作用域鏈一級一級地搜索標識符的過程。搜索過程始終從作用域鏈的前端開始,然後逐級向後回溯,直至找到標識符爲止(如果找不到標識符,通常會導致錯誤發生)。

每個環境都可以向上搜索作用域鏈,以查詢變量和函數名;但任何環境都不能通過向下搜索作用域鏈而進入另一個執行環境。

在編寫JavaScript代碼的過程中,不聲明而直接初始化變量是一個常見的錯誤做法,因爲這樣可能導致意外。在嚴格模式下,未聲明的變量會導致錯誤。

4.3垃圾收集

JavaScript具有自動垃圾收集機制。原理是:找出那些不再繼續使用的變量,然後釋放其佔用的內存。爲此,垃圾收集器會按照固定的時間間隔(或代碼執行中預定的收集時間)週期性地執行這一操作。

(1)標記清除

最常用的是標記清除。垃圾收集器爲所有存儲在內存中的變量加上標記,然後會去掉環境中的變量以及被環境中變量引用的變量的標記。而在此之後再被加上標記的變量被視爲準備刪除的變量,原因是環境中的變量已經無法訪問到這些變量了。最後,垃圾收集器完成內存清除的工作,銷燬那些帶標記的值並回收它們所佔用的內存空間。

(2)引用計數

不太常用的垃圾收集策略是引用計數。跟蹤記錄每個值被引用的次數,被賦值時+1,包含對這個值引用的變量又取得了另外一個值,-1,當這個值但引用變爲0時,回收內存空間。

 

爲了確保佔用最少但內存讓頁面獲得更好但性能。而優化內存佔用但最佳方式,就是爲執行中但代碼只保存必要的數據。一旦數據不再使用,最好通過將其值設爲null,來說釋放其引用——這個做法叫做解除引用(dereferencing)。解除引用的真正作用是讓值脫離執行環境,以便垃圾收集器下次運行時將其回收。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章