关于深拷贝和浅拷贝的我的理解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; }