理解
都是对原本的对象进行一份复制,差异如下:
-
浅拷贝 创建一个新对象,如果属性是基本类型,拷贝的就是基本类型的值,如果属性是引用类型,拷贝的就是内存地址 。这个内存地址指向同一个堆内存,如果其中一个对象改变了堆内存存放的值,那么所有的对象都会受到影响。 -
深拷贝 创建一个新对象,如果属性是基本类型,拷贝的就是基本类型的值,如果属性是引用类型,则从堆内存中开辟一个新的区域存放该引用类型指向的堆内存中的值,且修改新对象的值不会影响原对象。
放两张我做的蹩脚图加深一下理解:
总而言之,浅拷贝只复制指向某个对象的指针,而不复制对象本身,新旧对象还是共享同一块内存。但深拷贝会另外创造一个一模一样的对象,新对象跟原对象不共享内存,修改新对象不会改到原对象。
代码实现
浅拷贝实现方法
- 展开运算符…
ES6语法,可以非常方便的实现浅拷贝。
const obj1 = {
name: "icy",
age: 20,
hobbies: ["eat", "sleep", "game"],
};
const obj2 = { ...obj1 };
console.log(obj2);
输出: 为了验证是浅拷贝,我们改变一下obj中数组的第一项的值,然后再输出ojb1:
const obj1 = {
name: "icy",
age: 20,
hobbies: ["eat", "sleep", "game"],
};
const obj2 = { ...obj1 };
obj2.hobbies[0] = "play";
console.log("修改后obj2", obj2);
console.log("修改后obj1", obj1);
输出结果: obj1和obj2都受到了影响,验证了浅拷贝。
- Object.assign() 方法
该方法用于合并对象,用法 object.assign(合并的对象,合并的对象…) 支持传入多个对象,用逗号分隔,返回值为合并后的新对象 。
const obj1 = {
name: "icy",
age: 20,
hobbies: ["eat", "sleep", "game"],
};
const obj2 = Object.assign({}, obj1);
console.log(obj2);
- 函数库lodash的_.clone方法
lodash中文文档:https://www.lodashjs.com/ 需要node环境,下载npm包。 全局安装: npm i -g npm 安装到依赖中: npm i --save lodash
import _ from "lodash";
const obj1 = {
name: "icy",
age: 20,
hobbies: ["eat", "sleep", "game"],
};
const obj2 = _.clone(obj1);
console.log(obj2);
console.log(obj1.hobbies === obj2.hobbies);
结果如下:
- 数组合并方法 concat()
该方法用于数组合并,合并的数组.concat(被合并的数组…) 参数可有多个,用逗号分隔,返回合并后的数组。 原理:用原数组去合并一个空数组,返回合并后的数组。
const arr1 = [ 1,2,3,{name:'icy',gender:'美少女']
const arr2 = arr1.concat()
- 数组剪裁方法 slice()
该方法用途很多,可对数组进行增删,剪裁操作。 mdn上slice介绍:https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Array/slice
const arr1 = [ 1,2,3,{name:'icy',gender:'美少女']
const arr2 = arr1.slice()
深拷贝的实现方式
- JSON.parse(JSON.stringify())
利用JSON.stringfy()将对象转为json格式的字符串,再利用JSON.parse()将json格式的字符串转为对象,转换后的对象是一个新对象。
const obj1 = {
name: "icy",
age: 22,
gender: "美少女",
hobbies: ["eat", "sleep", "game"],
};
const obj2 = JSON.parse(JSON.stringify(obj1));
console.log(obj2);
输出: 验证一下深拷贝: 改变obj2的hobbies[0],看下对obj1是否有影响:
obj2.hobbies[0] = "看动漫";
console.log("obj1===", obj1);
console.log("obj2===", obj2);
结论:没有影响,深拷贝成功。
- 注意点:
这种方法虽然简单方便,可以实现数组或对象的深拷贝。但不能处理函数 和正则 ,因为这两者基于JSON.stringify和JSON.parse处理后,得到的正则就不再是正则(变为空对象),得到的函数就不再是函数(变为null)了。 比如:
const obj1 = {
name: "icy",
age: 22,
gender: "美少女",
hobbies: ["eat", "sleep", "game"],
watchComic: () => {
console.log("icy 我不做人啦");
},
regx: /^icy{3}$/g,
};
const obj2 = JSON.parse(JSON.stringify(obj1));
console.log("obj2===", obj2);
输出:
函数没了,正则变为空对象。
- 函数库lodash的_.cloneDeep方法
lodash提供了cloneDeep方法实现深拷贝
import _ from "lodash";
const obj1 = {
name: "icy",
age: 20,
hobbies: ["eat", "sleep", "game"],
};
const obj2 = _.clone(obj1);
console.log(obj2);
console.log(obj1.hobbies === obj2.hobbies);
const obj3 = _.cloneDeep(obj1);
console.log("obj3==", obj3);
console.log(obj1.hobbies === obj3.hobbies);
输出结果对比: 3. jQuery.extend()方法 jQuery提供了extend方法可以实现深拷贝。
$.extend(deepCopy, target, object1, [objectN])
import $ from "jquery"
const obj1 = {
name: "icy",
age: 20,
hobbies: ["eat", "sleep", "game"],
};
var obj2 = $.extend(true, {}, obj1);
- 手写递归方法
递归方法实现深度克隆原理:遍历对象、数组直到里边都是基本数据类型,然后再去复制,就是深度拷贝。
下面是大佬写的,考虑到了,Date,正则,函数,对象循环引用(自己引用自己)的一个深拷贝递归:
function deepClone(obj, hash = new WeakMap()) {
if (obj === null) return obj;
if (obj instanceof Date) return new Date(obj);
if (obj instanceof RegExp) return new RegExp(obj);
if (typeof obj !== "object") return obj;
if (hash.get(obj)) return hash.get(obj);
let cloneObj = new obj.constructor();
hash.set(obj, cloneObj);
for (let key in obj) {
if (obj.hasOwnProperty(key)) {
cloneObj[key] = deepClone(obj[key], hash);
}
}
return cloneObj;
}
let obj = { name: 1, address: { x: 100 } };
obj.o = obj;
let d = deepClone(obj);
obj.address.x = 200;
console.log(d);
参考文章:https://segmentfault.com/a/1190000020255831 https://juejin.cn/post/6844904197595332622
|