深拷貝和淺拷貝都是針對的引用類型,
JS中的變量類型分為值類型(基本類型)和引用類型;
對值類型進行復制操作會對值進行一份拷貝,而對引用類型賦值,則會對地址進行拷貝,最終兩個變量指向同一份數據
一、先來看看JS中的數據類型
let x = 1; //number類型 let x = 0.1; //number類型,JS不區分整數值和浮點數值 let x = "hello world"; //由雙引號內文本構成字符串 let x = 'javascript'; //單引號內文本同樣可以構成字符串 let x = true; // boolean 布爾類型 let x = null; let x = undefined; //null和undefined很相似,是特殊的類型
JS 中數據分為兩種類型:
- 原始數據類型
- number
- string
- boolean
- null
- undefined
- 對象數據類型
- array 數組 特殊對象類型
- function 函數 特殊對象類型
- object 對象
還有 undefined 和 null,此處暫不討論
object對象需要注意的點:
- 對象是可變的,即值是可以修改的
- 對象的比較並非值得比較
比如:var a = [], b = [];
a == b; //false,只有在引用相同(指向的地址相同)時,兩個只才會相等
由此可以延伸出 深拷貝和淺拷貝 的問題。
=========================================================================
我們的困惑:
1. 看着相等,卻又不等
2. 想要不等,卻又相等
那么造成這樣問題的原因在哪呢?
> 對象的引用
> 引用只會對地址進行賦值, 所以
1. 不同的變量 a 和 b,他們的地址不同,即使數據相同,本身也不會相等,這是造成困惑一的原因;
2. 而變量 aa 和 bb 指向同一地址,當該地址的數據改變時,所有使用該地址的變量全部改變(同一數據),這是造成困惑二的原因
達不到我們想要的效果,怎么辦呢?
二、引用(對象)數據類型的賦值和比較問題
解決辦法: 笨辦法,也是唯一的方式,既然對象數據類型 是由基本數據類型組成的,而基本數據類型可以正常賦值、比較,那我們就把對象類型變成一個個的基本類型進行操作
方法一: 遍歷對象中的內容,一個一個的進行賦值,這樣只進行一層拷貝的方式了,就是淺拷貝
// 淺拷貝方法 function shallowClone(source) { let target = {}; for(leti in source) { if (source.hasOwnProperty(i)) { target[i] = source[i]; } } return target; }
方法二: 相對於一層拷貝的淺拷貝,無線層次的拷貝叫做 深拷貝
// 簡單深拷貝 function clone(source) { let target = {}; for(let i in source) { if (source.hasOwnProperty(i)) { if (typeof source[i] === 'object') { target[i] = clone(source[i]); // 判斷仍是對象,就進行遞歸 } else { target[i] = source[i]; } } } return target; }
上面 clone 方法 和 shallowClone 方法的 區別就是 多了 遞歸
但是仍然有些問題需要注意:
- 參數需要檢驗
- 判斷是否對象的邏輯不夠嚴謹
- 需要考慮數組的情況
暫不細說,判斷對象可以用此方法:
// 更嚴謹的判斷對象的方法 function isObject(x) { return Object.prototype.toString.call(x) === '[object Object]'; }
當然我們也可以參考其他方法或使用插件
比如: 簡單粗暴的 JSON.parse(JSON.stringify(oldObj))
比如: ES6的assign方法(淺拷貝)
比如: 通過immutableJS實現深拷貝
三、最后
無論 淺拷貝,還是深拷貝 都會帶來性能問題(平白的需要遍歷,只是重新賦值)
所以我們對象最好寫的淺一點,精簡一點。。。
詳細可以看這篇: https://yanhaijing.com/javascript/2018/10/10/clone-deep/