JavaScript的記憶體空間
在JavaScript中,每一個資料都需要一個記憶體空間。記憶體空間分為兩種,棧記憶體(stack)與堆記憶體(heap)
棧是系統自動分配的記憶體空間,由系統自動釋放,堆則是動態分配的記憶體,大小不定不會自動釋放。
基礎資料型別
JavaScript中的基礎資料型別,這些值都有固定的大小,儲存在
棧記憶體中,由系統自動分配儲存空間
在棧記憶體空間的值,我們可以直接進行操作,因此基礎資料型別都是按照值訪問
在棧記憶體中的資料發生複製的行為時,系統會自動為新變數開闢一個新的記憶體空間,當複製執行後,兩個記憶體空間的值就互不影響,改變其中一個不會影響另一個
棧記憶體空間資料複製示例
var a = `I am variable a`;
var b = a;
console.log(b); //`I am variable a`
b = `I am variable b`;
console.log(a); //`I am variable a`
console.log(b); //`I am variable b`
複製程式碼
引用資料型別
引用型別的值是儲存在
堆記憶體中的物件,在JavaScript中我們不能直接操作物件的堆記憶體空間。因為引用型別的值都是按引用訪問的,所以在操作物件時,實際上是操作物件的引用而不是實際的物件。
引用可以理解為儲存在棧記憶體中的一個地址,該地址指向堆記憶體中的一個實際物件
引用型別值的複製,系統會為新的變數自動分配一個新的
棧記憶體空間這個
棧記憶體空間儲存著與被複制變數相同的指標,儘管他們在
棧記憶體中的記憶體空間的位置互相獨立但是在
堆記憶體中訪問到的物件實際上是同一個,因此,當我們改變其中一個物件的值時,實際上就是改變原來的物件
棧記憶體空間儲存指標(地址),堆記憶體空間儲存實際的物件,我們通過變數訪問物件時,實際上訪問的是物件的引用(地址)
記憶體中的棧區域存放變數(基本型別的變數包括變數宣告和值)以及指向堆區域儲存位置的指標(引用型別的變數包括變數宣告和指向內容的指標)
var a = {
name : `I am object a`,
type : 'object'
}
var b = a;
console.log(b);
// {name: "I am object a", type: "object"}
b.name = `I am object b`;
console.log(a);
// {name: "I am object b", type: "object"}
console.log(b);
// {name: "I am object b", type: "object"}
複製程式碼
基本型別總結
基本資料型別
:
包括:null、undefined、number、string、boolean、symbol(es6)
存放位置:記憶體中的棧區域中
比較:值的比較,判斷是否相等,如果值相等,就相等。一般使用===進行比較,因為==會進行型別的轉換
拷貝:賦值(通過(=)賦值操作符 賦值),賦值完成後,兩個變數之間就沒有任何關係了,改變其中一個變數的值對另一個沒有任何影響
引用型別總結
引用資料型別
:
包括:陣列、物件、函式
存放位置:記憶體的棧區域中存放變數和指標,堆區域儲存實際的物件
比較:是引用的比較(就是地址的比較,變數在棧記憶體中對應的指標地址相等就指向同一個物件)判斷是否為同一個物件,示例如下
變數a和變數b的引用不同,物件就不是同一個物件
var a = {name:'Jay'};
var b = {name:'Jay'};
a===b //false
複製程式碼
我們對JavaScript中引用型別進行操作的時候,都是操作其物件的引用(儲存在棧記憶體中的指標)
賦值、深拷貝和淺拷貝 (Assignment, deep copy and shallow copy)
賦值:兩個變數的值(指標)都指向同一個物件,改變其中一個,另一個也會受到影響
所謂拷貝就是複製,通過複製原物件生成一個新的物件
淺拷貝
:重新在堆記憶體中開闢一個空間,拷貝後新物件獲得一個獨立的基本資料型別
資料,和原物件共用一個原物件內的引用型別
資料,改變基本型別
資料,兩個物件互不影響,改變其中一個物件內的引用型別
資料,另一個物件會受到影響
var obj = {
name: 'Jay Chou',
age: 32,
song:{
name:'發如雪',
year:2007
}
}
var obj1 = obj;
function shallowCopy(obj){
var scObj = {};
for(var prop in obj){
if(obj.hasOwnProperty(prop)){
scObj[prop] = obj[prop]
}
}
return scObj;
}
var obj2 = shallowCopy(obj);
console.log(obj === obj1,'obj === obj1','賦值');
console.log(obj === obj2,'obj === obj2','淺拷貝');
// true "obj === obj1" "賦值"
// false "obj === obj2" "淺拷貝"
console.log(obj.song === obj2.song);
//true
obj2.song.name='雙截棍';
obj2.name='Jay';
console.log(obj)
// {name: "Jay Chou", age: 32, song: {name:'雙截棍',year:2007}}
console.log(obj1);
// {name: "Jay Chou", age: 32, song: {name:'雙截棍',year:2007}}
console.log(obj2);
{name: "Jay", age: 32, song: {name:'雙截棍',year:2007}}
console.log(obj===obj1)
//true
console.log(obj===obj2)
//false
複製程式碼
深拷貝
:不論是物件內的基本型別還是引用型別
都被完全拷貝,拷貝後兩個物件互不影響
一種比較簡單實現方法是使用var dcObj = JSON.parse(JSON.stringify(obj))
var obj = {
name: 'Jay Chou',
age: 32,
song:{
name:'發如雪',
year:2007
}
}
var dcObj=JSON.parse(JSON.stringify(obj));
console.log(dcObj);
// {name: "Jay Chou", age: 32, song: {name:'發如雪',year:2007}}
console.log(dcObj.song === obj.song);
//false
dcObj.name='Jay';
dcObj.song.name='雙截棍';
console.log(obj);
// {name: "Jay Chou", age: 32, song: {name:'發如雪',year:2007}}
console.log(dcObj);
//{name: "Jay", age: 32, song: {name:'雙截棍',year:2007}}
複製程式碼
比較:賦值、深拷貝、淺拷貝
賦值:新物件仍然指向原物件,改變新物件的基本型別
和引用型別
的值都會
使原物件對應的值一同改變
淺拷貝:改變新物件基本型別
的值不會
使原物件對應的值一起改變,但是改變新物件引用型別
的值會
使原物件對應的值一同改變
深拷貝:改變新物件基本型別
和引用型別
的值,都不會影響原物件,兩者互相獨立,互不影響