什麼是對象的拷貝?
將一個對象賦值給另一個對象, 咱們稱之爲對象的拷貝javascript
什麼是深拷貝, 什麼是淺拷貝?
咱們假設將A對象賦值給B對象前端
淺拷貝是指, 修改B對象的屬性和方法會影響到A對象的屬性和方法, 咱們稱之爲淺拷貝java
如下兩種狀況都屬於淺拷貝:數組
1、默認狀況下對象之間的直接賦值都是淺拷貝函數
let A = { name: 'zyx', age: 20 } let B = A console.log(B) // {name: "zyx", age: 20} //修改B的 name 屬性 B.name = 'ls' //A 也收到影響 console.log(A) // {name: "ls", age: 20} console.log(B) // {name: "ls", age: 20}
賦值操做(包括對象做爲參數、返回值),不會開闢新的內存空間,他只是賦值了對象的引用.也就是除了B這個名字以外,沒有其餘的內存開銷,修改了A也就影響了B,修改了B,也就影響了A.優化
如圖所示:prototype
2、若是對象的屬性包含了引用數據類型(數組、對象),那麼哪怕不是直接賦值操做,而是開闢了一層新的內存空間,也就是說只拷貝了A對象的一層,這仍然屬於淺拷貝。code
let A = { name: 'ls', age: 20, hobbies: ['dance','basketball','read'], dogs:{ name: '大黃', color: 'yellow' } } let B = {} //定義一個函數,把A對象的屬性複製一份給B function extend(obj1,obj2){ for(var key in obj1){ obj2[key] = obj1[key] } } extend(A,B) //修改B對象中的引用類型數據 ,A對象也收到影響 B.dogs.color = 'red' B.hobbies[0] = 'sing' console.log(B) console.log(A)
運行截圖以下:修改B對象中的引用類型數據 ,A對象也收到影響,屬於淺拷貝對象
三、ES6中新增的 Object.assign() 也是對象的淺拷貝blog
Object.assign
方法用於對象的合併,將源對象(source)的全部可枚舉屬性,複製到目標對象(target)。 Object.assign
方法的第一個參數是目標對象,後面的參數都是源對象。 注意,若是目標對象與源對象有同名屬性,或多個源對象有同名屬性,則後面的屬性會覆蓋前面的屬性。
const obj1 = {a: {b: 1}}; const obj2 = Object.assign({}, obj1); obj1.a.b = 2; obj2.a.b // 2
上面代碼中,源對象obj1
的a
屬性的值是一個對象,Object.assign
拷貝獲得的是這個對象的引用。這個對象的任何變化,都會反映到目標對象上面。
四、擴展運算符(...)
利用擴展運算符能夠在構造字面量對象時,進行克隆或者屬性拷貝 ,屬於淺拷貝
var obj = {a:1,b:{c:1}} var obj2 = {...obj}; obj.a=2; console.log(obj); //{a:2,b:{c:1}} console.log(obj2); //{a:1,b:{c:1}} obj.b.c = 2; console.log(obj); //{a:2,b:{c:2}} console.log(obj2); //{a:1,b:{c:2}}
五、Array.prototype.slice()
slice() 方法返回一個新的數組對象,這一對象是一個由 begin和 end(不包括end)決定的原數組的淺拷貝。原始數組不會被改變。
深拷貝是指, 修改B對象的屬性和方法不會影響到A對象的屬性和方法, 咱們稱之爲深拷貝
如下兩種狀況都屬於深拷貝:
一、默認狀況下一個對象的屬性若是是基本數據類型, 那麼進行復制(拷貝),都是深拷貝
若是A對象的屬性都是基本數據類型(Number、String等),此時要想深拷貝一份A給B,該怎麼作呢,在這種要拷貝的對象A只有基本類型的數據時,只須要在內存中開闢一塊空間存儲B就好了。
let A = { name: 'zyx', age: 20 } let B = {} //定義一個函數,把A對象的屬性複製一份給B function extend(obj1,obj2){ for(var key in obj1){ obj2[key] = obj1[key] } } extend(A,B) console.log(B) // {name: "zyx", age: 20} B.name = 'ls' console.log(B) // {name: "ls", age: 20} console.log(A) // {name: "zyx", age: 20}
這樣就實現了深拷貝,以下圖所示:
二、若是要拷貝的對象自己又包含了引用數據類型,即對象又包含數組或者對象,層層嵌套的狀況下,想要實現對象的深拷貝,能夠採用遞歸的方式進行深拷貝。
let A = { name: 'ls', age: 20, hobbies: ['dance','basketball','read'], dogs:{ name: '大黃', color: 'yellow' } } let B = {} //定義一個函數,把A對象的屬性複製一份給B function extend(obj1,obj2){ for(var key in obj1){ var item = obj1[key] if(item instanceof Array){ obj2[key] = [] extend(item,obj2[key]) }else if(item instanceof Object){ obj2[key] = {} extend(item,obj2[key]) }else{ obj2[key] = item } } } extend(A,B) B.dogs.color = 'red' B.hobbies[0] = 'sing' console.log(B) console.log(A)
運行發現,修改B對象的引用數據類型,不會影響到A對象,完成深拷貝
咱們能夠對深拷貝的代碼進行封裝優化
function deepClone(obj){ let cloneObj = {} for(let key in obj){ if(typeof obj[key] === 'object'){ cloneObj[key] = deepClone(obj[key]) }else{ cloneObj[key] = obj[key] } } return cloneObj }
三、經過JSON.stringify實現深拷貝
JSON.stringify()是目前前端開發過程當中最經常使用的深拷貝方式,原理是把一個對象序列化成爲一個JSON字符串,將對象的內容轉換成字符串的形式再保存在磁盤上,再用JSON.parse()反序列化將JSON字符串變成一個新的對象。
var obj1 = { a:1, b:[1,2,3] } var str = JSON.stringify(obj1) var obj2 = JSON.parse(str) console.log(obj2); //{a:1,b:[1,2,3]} obj1.a=2 obj1.b.push(4); console.log(obj1); //{a:2,b:[1,2,3,4]} console.log(obj2); //{a:1,b:[1,2,3]}