關於深拷貝和淺拷貝的我的理解javascript
1、深拷貝和淺拷貝區別java
如何區分深拷貝與淺拷貝,簡單點來講,就是假設B複製了A,當修改A時,看B是否會發生變化,若是B也跟着變了,說明這是淺拷貝,拿人手短,若是B沒變,那就是深拷貝,自食其力。typescript
具體可看這篇文章:https://www.jianshu.com/p/1c142ec2ca45數組
示例:ruby
let obj = { name: "hahah", age: 18, sex: "男", } // 複雜數據類型 賦值至關因而 賦引用地址 const newObj = obj; // 修改newObj其中一個值 , 另外一個obj也發生改變 newObj.name = "hahahahahahah"; console.log(obj, newObj);
輸出結果:兩個複雜數據類型的 name 值所有改變 ,複雜數據類型 = 賦值只是 引用地址的傳遞 ,指向的仍是相同的引用。bash
簡單的第一層淺拷貝:app
let obj = { name: "lili", age: 18, sex: "男", arr: [1, "2", "3"], obj: { name: "hahaha", sex: "女", } }; // 方法 一 // let newObj = Object.assign({}, obj); // 方法 二 let newObj = { ...obj }; // 再次改變時就是 改變不同的數據 newObj.name = "hahaha"; newObj.obj.sex = "男"; newObj.arr[1] = 10; console.log(obj, newObj);
輸出結果:能夠看到第一層的 name 沒有互相影響 ,可是更深層次的 arr 數組 和 obj 對象 仍是發生了相同改變,這仍是一個淺拷貝。咱們可使用不少方式在js中複製數據,好比 擴展運算符...,Object.assign, Object.freeze,slice, concat, map,filter, reduce等方式進行復制,ide
這裏能夠得出一個結論:淺拷貝,就是隻拷貝第一層數據,更深層的數據仍是同一個引用。函數
2、簡單數據類型(基本數據類型)spa
若是是基本數據類型,名字和值都會儲存在棧內存中
簡單數據類型能夠隨意賦值都不會相互影響。
3、複雜數據類型(引用數據類型)
複雜數據類型的存儲方法:存儲空間分爲堆空間 和 棧空間 ,複雜數據類型的值存在於 堆空間中 ,而名(www.taobaoosx.com,obj)則存在於棧空間中 ,堆空間返回一個引用地址給 名 ,名根據引用地址去訪問 棧空間中的值。
圖示:
當b=a進行拷貝時,其實複製的是a的引用地址,而並不是堆裏面的值。
此時 a 和 b 指向的是 堆空間的同一個地址。而當咱們a[0]=1時進行數組修改時,因爲a與b指向的是同一個地址,因此天然b也受了影響,這就是所謂的淺拷貝了。
順着往下想能夠獲得這麼一個結論:在堆內存中也開闢一個新的內存專門爲b存放值,就像基本類型那樣,豈不就達到深拷貝的效果了 。
4、實現深拷貝的方法
一、for in 循環實現深拷貝
let obj = { name: "hahaha", age: 18, objChild: { sex: "男", addres: "深圳", arr: [1, 2, 3, "10", "12", { aaa: "aaa", bbb: "bbb" }], }, func: function () { console.log("我是一個函數"); } } function simpleCopy(obj) { // 判斷 obj 爲數組仍是對象 let newObj = Array.isArray(obj) ? [] : {} // 遍歷該對象 for (let i in obj) { // 判斷 該對象是否 真的包含 i 這個鍵 if (obj.hasOwnProperty(i)) { // 判斷 obj[i] 是否爲 對象或者數組 if (obj[i] && (typeof obj[i]) === "object") { // 是對象遞歸再次執行 newObj[i] = simpleCopy(obj[i]); } else { // 不是直接簡單賦值 newObj[i] = obj[i]; } } } return newObj; } let newObj = simpleCopy(obj); newObj.name = "ccccc"; newObj.objChild.sex = "女"; newObj.objChild.arr[5].aaa = "小帥"; newObj.func = () => { console.log("hahahahahahah"); } obj.func(); // 我是一個函數 newObj.func(); // hahahahahahah console.log(obj); console.log(newObj); console.log(newObj === obj); // false
輸出結果:能夠看到拷貝後的修改並無影響到原先 對象 ,新的對象的引用地址已經改變,與原先對象屬於兩個不一樣對象,此時 console.log(newObj === obj); // false
二、JSON.stringfiy和JSON.parse方法實現深拷貝
let obj = { name: "hahaha", age: 18, objChild: { sex: "男", addres: "深圳", arr: [1, 2, 3, "10", "12", { aaa: "aaa", bbb: "bbb" }], func: function () { console.log("我是一個函數"); } }, } function simpleCopy(obj) { let objClone = JSON.parse(JSON.stringify(obj)); return objClone; } let newObj = simpleCopy(obj); newObj.name = "ccccc"; newObj.objChild.sex = "女"; newObj.objChild.arr[5].aaa = "小帥"; newObj.func = () => { console.log("hahahahahahah"); } console.log(obj); console.log(newObj); console.log(newObj === obj); // false obj.objChild.func(); // 我是一個函數 newObj.objChild.func(); // 報錯
輸出結果:
**缺點: 沒法實現對對象中方法的深拷貝,會顯示爲www.taobaoosx.com,undefined **
三、經過jQuery的extend方法實現深拷貝
let newObj = $.extend(true, {}, obj); // true爲深拷貝,false爲淺拷貝
四、lodash函數庫實現深拷貝
let result = _.cloneDeep(test)
官網截圖:
五、Reflect法(與 for in 遍歷相似)
// 代理法function deepClone(obj) { // 判斷是否爲一個對象 if (!isObject(obj)) { throw new Error('obj 不是一個對象!') } let isArray = Array.isArray(obj) let cloneObj = isArray ? [...obj] : { ...obj } // 靜態方法 Reflect.ownKeys() 返回一個由目標對象自身的屬性鍵組成的數組。 Reflect.ownKeys(cloneObj).forEach(key => { cloneObj[key] = isObject(obj[key]) ? deepClone(obj[key]) : obj[key] }) return cloneObj }
六、手動實現深拷貝
let obj1 = { a: 1, b: 2}let obj2 = { a: obj1.a, b: obj1.b}obj2.a = 3;alert(obj1.a); // 1alert(obj2.a); // 3
七、Object.assign來實現深拷貝
// 只適用於只有一層複雜數據類型var obj = { a: 1, b: 2}var obj1 = Object.assign({}, obj); // obj賦值給一個空{}obj1.a = 3;console.log(obj.a);// 1
八、數組方法slice實現對數組的深拷貝
// 當數組裏面的值是基本數據類型,好比String,Number,Boolean時,屬於深拷貝// 當數組裏面的值是引用數據類型,好比Object,Array時,屬於淺拷貝var arr1 = ["1","2","3"]; var arr2 = arr1.slice(0); arr2[1] = "9";console.log("數組的原始值:" + arr1 );console.log("數組的新值:" + arr2 );
九、數組方法concat實現對數組的深拷貝
// 當數組裏面的值是基本數據類型,好比String,Number,Boolean時,屬於深拷貝var arr1 = ["1","2","3"];var arr2 = arr1.concat(); arr2[1] = "9";console.log("數組的原始值:" + arr1 );console.log("數組的新值:" + arr2 );// 當數組裏面的值是引用數據類型,好比Object,Array時,屬於淺拷貝var arr1 = [{a:1},{b:2},{c:3}];var arr2 = arr1.concat(); arr2[0].a = "9";console.log("數組的原始值:" + arr1[0].a ); // 數組的原始值:9console.log("數組的新值:" + arr2[0].a ); // 數組的新值:9
十、使用擴展運算符實現深拷貝
// 當value是基本數據類型,好比String,Number,Boolean時,是可使用拓展運算符進行深拷貝的 // 當value是引用類型的值,好比Object,Array,引用類型進行深拷貝也只是拷貝了引用地址,因此屬於淺拷貝 var car = {brand: "BMW", price: "380000", length: "5米"} var car1 = { ...car, price: "500000" } console.log(car1); // { brand: "BMW", price: "500000", length: "5米" } console.log(car); // { brand: "BMW", price: "380000", length: "5米" }
十一、Object.create()實現深拷貝
function deepClone(initalObj, finalObj) { var obj = finalObj || {}; for (var i in initalObj) { var prop = initalObj[i]; // 避免相互引用對象致使死循環,如initalObj.a = initalObj的狀況 if(prop === obj) { continue; } if (typeof prop === 'object') { obj[i] = (prop.constructor === Array) ? [] : Object.create(prop); } else { obj[i] = prop; } } return obj; }