赞
踩
相同:浅拷贝和深拷贝都是针对于像Object、Array类似的复杂对象。
区别:浅拷贝只是对对象的第一层属性进行复制,深拷贝可以对对象进行递归复制。
一、浅拷贝
理解: 浅拷贝复制的是对象的内存地址,子类的属性指向的是父类属性的内存地址,所以,当子类的属性修改后,父类的属性也随之被修改。
当改变父类属性值
当改变子类属性值
从以上代码可看出,不管是子类Obj1还是父类Obj发生改变,两者的属性值都会同时改变,这是因为它们同用了一个内存地址,故而不关任何一个发生变化都会改变内存地址,这就是浅拷贝。、
实现方式:
1.Object.assign()
Object.assign()
方法用于将所有可枚举属性的值从一个或多个源对象source
复制到目标对象。它将返回目标对象target
。但是Object.assign()进行的是浅拷贝,拷贝的是对象的属性的引用,而不是对象本身
- // 1.Object.assign(),注意:当obj只有一层时,是深拷贝
- var obj = { a: { a: 'july', b: 10 }, b: 2 };
- var obj1 = Object.assign({}, obj);
- obj.a.a = 'jack';
- console.log(obj, obj1);
2.Array.prototype.concat()
Array.prototype.concat () 返回一个由当前数组和其它若干个数组或者若干个非数组值组合而成的新数组。
- var obj = [1, 3, { a: 'july', b: 10 }];
- var obj1 = obj.concat();
- obj1[2].a = 'jack';
- console.log(obj, obj1);
3.Array.prototype.slice()
slice()
方法返回一个新的数组对象,这一对象是一个由 begin
和 end
决定的原数组的浅拷贝(包括 begin
,不包括end
)。原始数组不会被改变。
- var obj = [1, 3, { a: 'july', b: 10 }];
- var obj1 = obj.slice();
- obj1[2].a = 'jack';
- console.log(obj, obj1);
关于Array的slice和concat方法的补充说明:Array的slice和concat方法不修改原数组,只会返回一个浅复制了原数组中的元素的一个新数组。
- var obj = [1, 3, { a: 'july', b: 10 }];
- var obj1 = obj.slice();
- obj[0] = 5;
- console.log(obj, obj1);
二、深拷贝
理解:深拷贝开辟一个新的栈,两个对象属性完全相同,但是对应两个不同的地址,修改一个对象的属性,不会改变另一个对象的属性
实现方式:
1.JSON.parse(JSON.stringify())
用 JSON.stringify
把对象转换成字符串,再用JSON.parse
把字符串转换成新的对象,这种方法虽然可以实现数组或对象深拷贝,但是存在弊端,会忽略undefined、symbol和函数
- var obj = [1, 3, { a: 'july', b: 10 }];
- var obj1 = JSON.parse(JSON.stringify(obj));
- obj1[2].a = 'jack';
- console.log(obj, obj1);
2.使用函数库lodash
- var _ = require('lodash');
- var obj = { a: { a: 'july', b: 10 }, b: 2 };
- var obj1 = _.cloneDeep(obj);
- console.log(obj.a.a == obj1.a.a); //false
在使用require时,可能会出现以下错误:
这是由于从node.js 14版及以上版本中,require作为COMMONJS的一个命令已不再直接支持使用,所以我们需要导入createRequire命令才可以
解决方案:在require的代码前引入如下代码:
- import { createRequire } from 'module';
- const require = createRequire(import.meta.url);
3.jQuery.extend()
- const $ = require('jquery');
- var obj = { a: { a: 'july', b: 10 }, b: 2 };
- const obj1 = $.extend(true, {}, obj);
- console.log(obj.a.a == obj1.a.a); // false
4.手写循环递归
递归方法实现深度克隆原理:遍历对象、数组直到里边都是基本数据类型,然后再去复制,就是深度拷贝
- // 4.手写循环递归
- function deepClone(obj, hash = new WeakMap()) { //使用WeakMap()处理循环引用,防止进入死循环
- // 当传入的参数是null或者undefined时 利用null==undefined 直接返回,不进行拷贝操作
- if (obj === null) return obj;
- // 当传入的是日期格式的对象时 再将其拷贝一层
- if (obj instanceof Date) return new Date(obj); //instanceof 运算符用于检测构造函数的 prototype 属性是否出现在某个实例对象的原型链上。
- // 当传入的是正则表达式格式的对象时 再将其拷贝一层
- if (obj instanceof RegExp) return new RegExp(obj);
- // 当传入的是函数对象时 直接返回
- if (typeof obj !== "object") return obj;
- // 如果拷贝的对象是存在,直接从weakmap中返回即可
- if (hash.get(obj)) return hash.get(obj);
- // 如果拷贝的对象是存在,直接从weakmap中返回即可
- let cloneObj = new obj.constructor();
- // 找到的是所属类原型上的constructor,而原型上的 constructor指向的是当前类本身
- hash.set(obj, cloneObj);
- for (let key in obj) { // for...in循环会遍历obj原型上的属性
- if (obj.hasOwnProperty(key)) { // 通过obj.hasOwnProperty只遍历obj对象上的属性
- // 递归拷贝每一项的值
- cloneObj[key] = deepClone(obj[key], hash);
- }
- }
- return cloneObj;
- }
- function fn1(){
- return console.log("深拷贝");
- }
- var nowTime = new Date();
- var obj = [1, 3, { a: 'july', b: 10 }, [10,5], nowTime];
- // var obj = fn1 //函数需要单独复制
- var obj1 = deepClone(obj)
- console.log(obj1);
三、应用
1:从服务器fetch到数据之后我将其存放在store中,通过props传递给界面,如果需要对这堆数据进行修改,那涉及到的修改就一定有保存和取消,所以我们需要将这堆数据拷贝到其它地方。
2:当你想使用某个对象的值,在修改时不想修改原对象,那么可以用深拷贝弄一个新的内存对象。像es6的新增方法都是深拷贝,所以推荐使用es6语法。
小结
前提为拷贝类型为引用类型的情况下:
浅拷贝是拷贝一层,属性为对象时,浅拷贝是复制,两个对象指向同一个地址
深拷贝是递归拷贝深层次,属性为对象时,深拷贝是新开栈,两个对象指向不同的地址
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。