一、數據類型
在討論深淺拷貝之前,我們先說說數據類型,因為深淺拷貝與數據類型有關。
數據類型分為基本數據類型(String、Number、Boolean、Null、Undefined、Symbol (es6引入的一種類型) )和引用數據類型(Object、Array、Function)。
基本數據類型特點:直接存儲在棧中;
引用數據類型:它真實的數據是存儲在堆內存中,棧中存儲的只是指針,指向在堆中的實體地址。
二、淺拷貝、深拷貝
深淺拷貝只是針對Array與Object這樣的引用數據類型。簡單來說,淺拷貝只是拷貝了它在棧中存儲的指針,它們指向的都是同一個堆內存地址,所以淺拷貝在某些情況會造成改變數據后導致別的另一份數據也同步被改變的情況;而深拷貝是直接將堆內存中存儲的數據直接復制一份,不會有淺拷貝互相影響的問題。
三、淺拷貝的方法
1. Object.assign()
從上面的例子我們發現,用Object.assign()實現的淺拷貝,當拷貝對象中的字段第一層的值為基礎數據類型(如上面的a的值是個字符串,“a1”),那是不會被同步影響的;如果第一層的值還包含子對象(如上面b的值就是個對象,還包含了name字段),那就會被影響。所以如果被復制的object的每個字段第一層都是基礎數據類型,那就是深拷貝。
2. Array.prototype.concat()
3. Array.prototype.slice()
2和3的方式效果與1一樣,都是第一層的值為基礎數據類型不會被同步影響,否則就會。
四、深拷貝的方法
1. JSON.parse(JSON.stringify())
將一個對象先轉為json字符串,然后再轉回來,這樣可以實現深拷貝。
但是這個方法有個缺陷,可以實現對象或數組的深拷貝,但是不能處理函數,函數經過這樣處理后會變成null。
所以比較推薦的方法是:
1. 遞歸的方法,遍歷要克隆的每個字段,發現它的值是對象或數組后繼續遞歸。
function clone(target) { let res = undefined; if (Object.prototype.toString.call(target) === '[object Object]') { res = {}; } else if (Object.prototype.toString.call(target) === '[object Array]') { res = []; } else { return target; } for (let key in target) { let value = target[key]; if (Object.prototype.toString.call(value) === '[object Object]' || Object.prototype.toString.call(value) === '[object Array]') { res[key] = clone(value); } else { res[key] = value; } } return res; } let a = [1,2,3,{ a: "a1", b: { name: "張三" } }] let b = clone(a) b[3].b.name = "我是b" console.log(a,b)
2. 比較推薦的方法,引用lodash依賴,使用里面的cloneDeep()方法