JavaScript的記憶體空間、賦值和深淺拷貝
JavaScript的記憶體空間
在JavaScript中,每一個數據都需要一個記憶體空間。記憶體空間分為兩種,棧記憶體(stock)與堆記憶體(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}}
比較:賦值、深拷貝、淺拷貝
賦值:新物件仍然指向原物件,改變新物件的基本型別
和引用型別
的值都會
使原物件對應的值一同改變
淺拷貝:改變新物件基本型別
的值不會
使原物件對應的值一起改變,但是改變新物件引用型別
的值會
使原物件對應的值一同改變
深拷貝:改變新物件基本型別
和引用型別
的值,都不會影響原物件,兩者互相獨立,互不影響