什麼是淺拷貝?從基本型別和棧記憶體以及堆記憶體講起.
我們在js中最常用的賦值方式就是淺拷貝.如: 兩個物件a,b. 那麼 讓b等於a.
var b = a;
這就是最基本的淺拷貝了.那淺拷貝的表現形式是什麼呢? 解決這個問題我們要先談談js中的資料型別了.
在es5中有6中資料型別它們是:undefined,null,Boolean,Stirng,Number,Object.
在es6中新增一個原始的資料型別Symbol那麼js就有了7個資料型別了.
但是這些資料型別又可以分為基本資料型別和引用資料型別.
-
基本資料型別(undefined,null,Boolean,Stirng,Number):
如何理解呢?它們保持的變數名(如:a)都標示在棧記憶體中的實際的值.如 var a = 1; 那麼就是說a標示棧記憶體中的某一值為1的物件. -
引用資料型別(Object):
引用資料型別與基本資料型別不同點就是在"引用"這兩個字上面.它本質上表示在棧記憶體上的一條指標記錄.而指標所指的方向(指到堆記憶體裡面),就是那塊堆記憶體的物件,這才是引用型別真正的值.引用型別棧記憶體的表現其實就是一條指標記錄.
那麼究竟什麼是淺拷貝呢?
淺拷貝就是棧記憶體的拷貝. 比如: 我把 a 物件的值賦給 b物件 就是把我a在棧記憶體裡面的內容複製給b了.
var a = b
如果a是基本資料型別.那麼b在棧記憶體裡面獲得的就是一個和a一樣內容的東西(拷貝).他的內容是一個基本型別.
如果a是引用資料型別.那麼b在棧記憶體裡面獲得的就是一個和a一樣內容的東西(拷貝).它的內容是一個指標(指向同一塊對記憶體).!!!(淺拷貝的本質!) 既然b獲得是一條與a相同的指標記錄.那麼a和b本質上都連向同一塊堆記憶體了! 這就是淺拷貝
所以當我們改變b物件中某個屬性的值,a中的屬性值也隨之變化. 因為他們擁有相同的指標.
綜上淺拷貝就是說兩個物件拷貝的是棧記憶體中的內容; 基本型別就獲得的是對應的值,而引用型別獲得的就是指標!!!
指標是導致淺拷貝的表現的根本原因.
什麼是淺拷貝的表現. 就是 b 變化了 a也隨之變化. 且永遠 a===b
程式碼如下:
let a = {name: 'ldj',age: 30}
let b = a
b.age = 40
console.log(b)
console.log(a)
console.log(a === b)
什麼是深拷貝?
通過上面的描述我們知道所謂淺拷貝就是棧記憶體裡面的內容拷貝.而Object型別的話拷貝的就是指標記錄(指向同一塊對記憶體). 那深拷貝呢. 就是重新建一塊堆記憶體了,b物件的指標指向這塊新的堆記憶體.a和b指向了不同的堆記憶體(堆記憶體裡面的屬性的值如果是Object型別的話也指向新的堆記憶體).
深拷貝的方法:
- JSON.parse(JSON.stringify(obj)) 方法
let b = JSON.parse(JSON.stringify(a))
其實上面這一輪操作就是讓b的指標從新獲得一塊堆記憶體了.
- 遞迴遞迴去複製所有層級屬性
function deepClone(obj) { if (obj == null) return null; if (obj instanceof Date) return new Date(obj); if (obj instanceof RegExp) return new RegExp(obj); if (typeof obj !== 'object') return obj; let t = new obj.constructor for (let key in obj) { t[key] = deepClone(obj[key]) } return t; } 複製程式碼
使用遞迴,每一個層級的去查.如果是引用型別.那就new一個新的物件(new 出來的東西肯定得有一塊新的對記憶體來儲存吧.) 然後再把整個新物件return出去.
[誤區]還有其他深拷貝的方法了嗎?
可能很多朋友認為.
let b = Object.assign({}, a)
或者
let b = {...a, ...{}}
如果a是 Array 型別的值
let b = [].concat(a)
或者
let b = [...a, ...[]]
以上幾種方法其實指實現了第一層級的 '深'拷貝. 而沒有實現真正意義上的深! 可以這樣理解.它只調了deepCloneErr()方法一次 沒有去做遞迴!
第一次寫文章,文字差,沒配圖,請多包涵.
有錯誤請指正,
有疑問可共同探討.
謝謝.