內(nèi)存生命周期
不管什么程序語言,內(nèi)存生命周期基本是一致的:
- 分配你所需要的內(nèi)存
- 使用分配到的內(nèi)存(讀、寫)
- 不需要時將其釋放\歸還
在 JavaScript 中,最后一步是隱藏的、透明的。
JavaScript 的內(nèi)存分配
- JavaScript 在定義變量時就完成了內(nèi)存分配。
var a = 123; // 給數(shù)值變量分配內(nèi)存
var b = "azerty"; // 給字符串分配內(nèi)存
- 通過函數(shù)調(diào)用的內(nèi)存分配
var c = new Date(); // 分配一個 Date 對象
var d = document.createElement('div'); // 分配一個 DOM 元素
- 有些方法分配新變量或者新對象
var e = "azerty";
var f = e.substr(0, 3); // f 是一個新的字符串
// 因為字符串是不變量,JavaScript 可能決定不分配內(nèi)存,只是存儲了 [0-3] 的范圍。
值的使用
使用值的過程實際上是對分配內(nèi)存進(jìn)行讀取與寫入的操作。讀取與寫入可能是寫入一個變量或者一個對象的屬性值,甚至傳遞函數(shù)的參數(shù)。
當(dāng)內(nèi)存不再需要使用時釋放(垃圾回收)
引用計數(shù)
此算法把“對象是否不再需要”簡化定義為“對象有沒有其他對象引用到它”。如果沒有引用指向該對象(零引用),對象將被垃圾回收機(jī)制回收。
標(biāo)記清除
這個算法把“對象是否不再需要”簡化定義為“對象是否可以獲得”。
當(dāng)變量進(jìn)入環(huán)境時,我們會標(biāo)記為“進(jìn)入環(huán)境,并且給它一個標(biāo)示。從邏輯上講,永遠(yuǎn)不能釋放進(jìn)入環(huán)境的變量所占用的內(nèi)存,因為只要進(jìn)入到了相應(yīng)的環(huán)境,那么我們的瀏覽器就有可能用到它們,而當(dāng)變量離開環(huán)境時。則將其標(biāo)記為離開環(huán)境;
垃圾收集器在運行的時候會給存儲在內(nèi)存中的所有變量都加上標(biāo)記,然后,它會去掉環(huán)境中的變量及被環(huán)境中的變量引用的變量的標(biāo)記。而在此之后再被加上離開環(huán)境時標(biāo)記的變量講被視為準(zhǔn)備刪除的變量,原因是環(huán)境中的變量已經(jīng)無法訪問到這些變量了,最后,垃圾收集器完成內(nèi)存的清除工作。銷毀哪些帶標(biāo)記的值,并回收他們所占用的內(nèi)存空間。
內(nèi)存空間
- JavaScript的執(zhí)行上下文生成之后,會創(chuàng)建一個叫做變量對象的特殊對象,JavaScript的基礎(chǔ)數(shù)據(jù)類型往往都會保存在變量對象中。
var a = 20;
var b = a;
b = 30;
// 這時a的值是20
- JS的引用數(shù)據(jù)類型的值是保存在堆內(nèi)存中的對象。JavaScript不允許直接訪問堆內(nèi)存中的位置,因此我們不能直接操作對象的堆內(nèi)存空間。在操作對象時,實際上是在操作對象的引用而不是實際的對象。因此,引用類型的值都是按引用訪問的。
var m = { a: 10, b: 20 }
var n = m;
n.a = 15;
// 這時m.a的值是15
按值傳遞
按值傳遞就是把函數(shù)外部的值復(fù)制給函數(shù)內(nèi)部的參數(shù),就和把值從一個變量復(fù)制到另一個變量一樣。
var value = 1;
function foo(v) {
v = 2;
console.log(v); //2
}
foo(value);
console.log(value) // 1
當(dāng)傳遞 value 到函數(shù) foo 中,相當(dāng)于拷貝了一份 value,假設(shè)拷貝的這份叫 _value,函數(shù)中修改的都是 _value 的值,而不會影響原來的 value 值。
按引用的副本傳遞(按值傳遞)
按引用傳遞,就是傳遞對象的引用,函數(shù)內(nèi)部對參數(shù)的任何改變都會影響該對象的值,因為兩者引用的是同一個對象。
var obj = {
value: 1
};
function foo(o) {
o.value = 2;
console.log(o.value); //2
}
foo(obj);
console.log(obj.value) // 2
按引用傳遞是傳遞對象的引用,而函數(shù)中參數(shù)是傳遞對象的引用的副本!
var obj = {
value: 1
};
function foo(o) {
o = 2;
console.log(o); //2
}
foo(obj);
console.log(obj.value) // 1
修改 o.value,可以通過引用找到原值,但是直接修改 o,并不會修改原值。
結(jié)論
參數(shù)如果是基本類型是按值傳遞,如果是引用類型按引用的副本傳遞。
但是因為拷貝副本也是一種值的拷貝,所以在高程中也直接認(rèn)為是按值傳遞
所以ECMAScript中所有函數(shù)的參數(shù)都是按值傳遞的。