標籤:很多 boolean 資料 方式 記錄 not ring ble 如何
[From] http://www.imooc.com/article/4585
基本類型和參考型別
js中的變數雖然不區分類型,但是實際上Ecmascript包含兩種類型,基本類型和參考型別.
基本類型有5種:Undefined,Null,Boolean,Number,String,基本類型是按值訪問的,因為可以操作儲存在變數中的實際的值。
參考型別的值是儲存在記憶體中的對象。與其他語言不同,JavaScript 不允許直接存取記憶體中的位置,也就是說不能直接操作對象的記憶體空間。在操作對象時,實際上是在操作對象的引用(也稱為控制代碼)而不是實際的對象.
注意:字串字面量是基礎資料型別 (Elementary Data Type),這和其他的語言(例如java)有所區別.
js中函數參數的傳遞只有一種:值傳遞.請看下面的例子:
function setName (obj) { obj.name = ‘hello‘;}var p = new Object();setName(p);console.log(p.name); // hello
以上代碼中建立一個對象,並將其儲存在了變數p中。然後,這個變數被傳遞到setName()函數中之後就被複製給了obj。在這個函數內部,obj和p引用的是同一個對象。換句話說,即使這個變數是按值傳遞的,obj也會按引用來訪問同一個對象。於是,當在函數內部為obj添加name屬性後,函數外部的p也將有所反映;因為p指向的對象在堆記憶體中只有一個,而且是全域對象。有很多開發人員錯誤地認為:在局部範圍中修改的對象會在全域範圍中反映出來,就說明參數是按引用傳遞的。為了證明對象是按值傳遞的,我們再看一看下面這個經過修改的例子:
function setName (obj) { obj.name = ‘hello‘; obj = new Object(); obj.name = ‘gray‘;}var p = new Object();setName(p);console.log(p.name); // hello
這個例子與前一個例子的唯一區別,就是在setName()函數中添加了兩行代碼:一行代碼為obj重新定義了一個對象,另一行代碼為該對象定義了一個帶有不同值的name屬性。在把p傳遞給setName()後,其name屬性被設定為"hello"。然後,又將一個新對象賦給變數obj,同時將其name屬性設定為"gray"。如果p是按引用傳遞的,那麼p就會自動被修改為指向其name屬性值為"gray"的新對象。但是,當接下來再訪問person.name時,顯示的值仍然是"hello"。這說明:即使在函數內部修改了參數的值,但原始的引用仍然保持未變。實際上,當在函數內部重寫obj時,這個變數引用的就是一個局部對象了。而這個局部對象會在函數執行完畢後立即被銷毀。
可以把 ECMAScript 函數的參數想象成局部變數。
類型檢測
typeof運算子在檢測基本類型的時候非常有用,能夠識別出:string,number,boolean,undefined,但是對null和對象進行此運算得到的都是‘object‘.此操作符對於檢測對象來說沒有用(因為我們通常是想知道某值是不是對象,我們想知道它是什麼的執行個體).
對於檢測對象,我們有instanceof運算子:
result = variable instanceof constructor
如果對基礎資料型別 (Elementary Data Type)進行instanceof運算,得到的結果將是false,因為基本類型不是對象.
垃圾收集
垃圾收集機制其實非常簡單,周期性地找出不再繼續使用的變數,釋放其記憶體.標識無用變數的策略有以下的2種方式:
標記清除(mark-and-sweep)
絕大多數瀏覽器採用的垃圾收集機制.
當變數進入環境(例如,在函數中聲明一個變數)時,就將這個變數標記為“進入環境”。從邏輯上講,永遠不能釋放進入環境的變數所佔用的記憶體,因為只要執行流進入相應的環境,就可能會用到它們。而當變數離開環境時,則將其標記為“離開環境”。
可以使用任何方式來標記變數。比如,可以通過翻轉某個特殊的位來記錄一個變數何時進入環境,或者使用一個“進入環境的”變數列表及一個“離開環境的”變數列表來跟蹤哪個變數發生了變化。說到底,如何標記變數其實並不重要,關鍵在於採取什麼策略.
垃圾收集器在啟動並執行時候會給儲存在記憶體中的所有變數都加上標記(當然,可以使用任何標記方式)。然後,它會去掉環境中的變數以及被環境中的變數引用的變數的標記。而在此之後再被加上標記的變數將被視為準備刪除的變數,原因是環境中的變數已經無法訪問到這些變數了。最後,垃圾收集器完成記憶體清除工作,銷毀那些帶標記的值並回收它們所佔用的記憶體空間。
引用計數(reference counting)
追蹤記錄每個值被引用的次數。當聲明了一個變數並將一個參考型別值賦給該變數時,則這個值的引用次數就是1。如果同一個值又被賦給另一個變數,則該值的引用次數加1。相反,如果包含對這個值引用的變數又取得了另外一個值,則這個值的引用次數減 1。當這個值的引用次數變成 0 時,則說明沒有辦法再訪問這個值了,因而就可以將其佔用的記憶體空間回收回來。這樣,當垃圾收集器下次再運行時,它就會釋放那些引用次數為零的值所佔用的記憶體。
這種方式存在一個嚴重的問題循環參考.循環參考指的是對象 A 中包含一個指向對象 B 的指標,而對象 B 中也包含一個指向對象 A 的引用。
function problem() { var objectA = new Object(); var objectB = new Object(); objectA.someOtherObject = objectB; objectB.anotherObject = objectA;}
在這個例子中, objectA 和 objectB 通過各自的屬性相互引用;也就是說,這兩個對象的引用次數都是2。在採用標記清除策略的實現中,由於函數執行之後,這兩個對象都離開了範圍,因此這種相互引用不是個問題。但在採用引用計數策略的實現中,當函數執行完畢後,objectA 和 objectB 還將繼續存在,因為它們的引用次數永遠不會是0。假如這個函數被重複多次調用,就會導致大量記憶體得不到回收。為此,Netscape 在 Navigator 4.0中放棄了引用計數方式,轉而採用標記清除來實現其垃圾收集機制。可是,引用計數導致的麻煩並未就此終結。
IE 中有一部分對象並不是原生JavaScript對象。例如,其BOM和DOM中的對象就是使用C++以COM(Component Object Model,元件物件模型)對象的形式實現的,而 COM 物件的垃圾收集機制採用的就是引用計數策略。因此,即使IE的JavaScript引擎是使用標記清除策略來實現的,但JavaScript訪問的COM對象依然是基於引用計數策略的。換句話說,只要在 IE 中涉及 COM 物件,就會存在循環參考的問題。為瞭解決上述問題,IE9 把 BOM 和 DOM 對象都轉換成了真正的 JavaScript 對象。這樣,就避免了兩種垃圾收集演算法並存導致的問題,也消除了常見的記憶體流失現象。
[轉] javascript中的變數和記憶體回收