一、深拷貝與淺拷貝的概念
在進行JavaScript編程過程中,經常會涉及到對象的拷貝操作。對象的拷貝分為淺拷貝和深拷貝兩種方式。
淺拷貝是指將一個對象複製到另一個對象,產生一個新的對象,新對象和原對象之間的基本類型數據相互獨立,而對於引用類型的數據,依然指向原對象的存儲空間。
深拷貝是指將源對象的值逐個複製到一個新的對象上,並且遞歸地將引用類型的數據也逐個複製到新對象上。這樣,新對象和源對象就互相獨立,互不影響。
二、淺拷貝的實現方式
淺拷貝可以通過Object.assign()或展開運算符(…)來實現。
//使用Object.assign()實現淺拷貝 let obj1 = {a: 1, b: 2}; let obj2 = Object.assign({}, obj1); console.log(obj2); //{a: 1, b: 2} //使用展開運算符實現淺拷貝 let obj1 = {a: 1, b: 2}; let obj2 = {...obj1}; console.log(obj2); //{a: 1, b: 2}
值得注意的是,淺拷貝只會複製原對象的第一層數據,如果原對象的某個屬性是引用類型,那麼新對象將使用同一個引用類型數據。
let obj1 = {a: 1, b: [1,2,3]}; let obj2 = {...obj1}; obj2.b.push(4); console.log(obj1); //{a: 1, b: [1,2,3,4]} console.log(obj2); //{a: 1, b: [1,2,3,4]}
三、深拷貝的實現方式
實現深拷貝的方式有很多,常用的有遞歸拷貝、JSON.parse()和JSON.stringify()等。
1.遞歸拷貝
遞歸拷貝通過遞歸遍歷原對象,將原對象的每個子元素(屬性)都逐個複製到新對象上,並且對於原對象的每個子元素如果是引用類型數據,就遞歸拷貝這個引用類型數據。
//遞歸拷貝實現函數 function deepClone(obj) { if (typeof obj !== 'object' || obj === null) { return obj; } let cloneObj = Array.isArray(obj) ? [] : {}; for (let key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { cloneObj[key] = deepClone(obj[key]); } } return cloneObj; } //測試遞歸拷貝函數 let obj1 = {a: 1, b: [1,2,3], c: {d: 4}}; let obj2 = deepClone(obj1); obj2.b.push(4); obj2.c.d = 5; console.log(obj1); //{a: 1, b: [1,2,3], c: {d: 4}} console.log(obj2); //{a: 1, b: [1,2,3,4], c: {d: 5}}
遞歸拷貝雖然實現簡單,但是由於遞歸遍歷可能會導致棧溢出,因此它的性能和可用性並不好。下面介紹另外兩種方式。
2.JSON.parse()和JSON.stringify()
將對象轉換為JSON字符串,然後再將JSON字符串轉換為對象的方式可以實現深拷貝,JSON.parse()和JSON.stringfy()就是這樣一種方式。
let obj1 = {a: 1, b: [1,2,3], c: {d: 4}}; let obj2 = JSON.parse(JSON.stringify(obj1)); obj2.b.push(4); obj2.c.d = 5; console.log(obj1); //{a: 1, b: [1,2,3], c: {d: 4}} console.log(obj2); //{a: 1, b: [1,2,3,4], c: {d: 5}}
需要注意的是,通過JSON.stringify()和JSON.parse()方式實現深拷貝,存在一些限制:
- 這種方式只能複製可枚舉屬性,而不能複製不可枚舉屬性和Symbol類型的屬性。
- 這種方式不能複製對象的方法。
- 如果原對象的某個屬性值為undefined、function、symbol,那麼新對象中將不包含這些屬性。
- 如果原對象有循環引用的情況(即某個屬性引用了對象本身),那麼用JSON.stringify()將會拋出異常。
四、拷貝的性能問題
因為深拷貝要遞歸遍歷整個對象,因此深拷貝的效率一般要比淺拷貝慢得多。在代碼效率要求很高的情況下,應該優先選擇使用淺拷貝而不使用深拷貝。深拷貝可以通過緩存已經處理過的對象來提高效率。
//使用Map緩存已經處理過的對象 function deepCloneWithMap(obj, map = new Map()) { if (typeof obj !== 'object' || obj === null) { return obj; } if (map.has(obj)) { return map.get(obj); } let cloneObj = Array.isArray(obj) ? [] : {}; map.set(obj, cloneObj); for (let key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { cloneObj[key] = deepCloneWithMap(obj[key], map); } } return cloneObj; } //測試遞歸拷貝函數 let obj1 = {a: 1, b: [1,2,3], c: {d: 4}}; let obj2 = deepCloneWithMap(obj1); obj2.b.push(4); obj2.c.d = 5; console.log(obj1); //{a: 1, b: [1,2,3], c: {d: 4}} console.log(obj2); //{a: 1, b: [1,2,3,4], c: {d: 5}}
代碼示例
//淺拷貝示例 let obj1 = {a: 1, b: [1,2,3]}; let obj2 = {...obj1}; obj2.b.push(4); console.log(obj1); //{a: 1, b: [1,2,3,4]} console.log(obj2); //{a: 1, b: [1,2,3,4]} //遞歸拷貝示例 function deepClone(obj) { if (typeof obj !== 'object' || obj === null) { return obj; } let cloneObj = Array.isArray(obj) ? [] : {}; for (let key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { cloneObj[key] = deepClone(obj[key]); } } return cloneObj; } let obj1 = {a: 1, b: [1,2,3], c: {d: 4}}; let obj2 = deepClone(obj1); obj2.b.push(4); obj2.c.d = 5; console.log(obj1); //{a: 1, b: [1,2,3], c: {d: 4}} console.log(obj2); //{a: 1, b: [1,2,3,4], c: {d: 5}} //JSON.parse和JSON.stringify示例 let obj1 = {a: 1, b: [1,2,3], c: {d: 4}}; let obj2 = JSON.parse(JSON.stringify(obj1)); obj2.b.push(4); obj2.c.d = 5; console.log(obj1); //{a: 1, b: [1,2,3], c: {d: 4}} console.log(obj2); //{a: 1, b: [1,2,3,4], c: {d: 5}} //使用Map緩存已經處理過的對象的遞歸拷貝示例 function deepCloneWithMap(obj, map = new Map()) { if (typeof obj !== 'object' || obj === null) { return obj; } if (map.has(obj)) { return map.get(obj); } let cloneObj = Array.isArray(obj) ? [] : {}; map.set(obj, cloneObj); for (let key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { cloneObj[key] = deepCloneWithMap(obj[key], map); } } return cloneObj; } let obj1 = {a: 1, b: [1,2,3], c: {d: 4}}; let obj2 = deepCloneWithMap(obj1); obj2.b.push(4); obj2.c.d = 5; console.log(obj1); //{a: 1, b: [1,2,3], c: {d: 4}} console.log(obj2); //{a: 1, b: [1,2,3,4], c: {d: 5}}
原創文章,作者:GLNQZ,如若轉載,請註明出處:https://www.506064.com/zh-hk/n/372284.html