为什么要深拷贝?
JavaScript存储对象都是存地址的,所以浅拷贝会导致obj1和obj2指向同一块内存地址。改变其中一方的内容,都是在原来的内存上做修改,导致拷贝对象和源对象都发生改变,而深拷贝是开辟一块新的内存地址,将源对象的各个属性逐个复制进去。对拷贝对象和源对象各自的操作互不影响。
如何实现深拷贝
利用JSON.parse(JSON.stringify())
JSON.parse(JSON.stringify())实现深拷贝,其原理就是利用JSON。Stringify将js对象序列化,再反序列化还原对象。序列化的作用是存储(对象本身存储的只是一个地址映射,如果断电,对象将不复存在,因此需将对象的内容转换成字符串的形式再保存在磁盘上)和传输。
弊端:
1.如果obj里面有时间对象,用其处理后,返回的只是字符串形式的数据。
2.如果obj里有RegExp、Error对象,则序列化的结果会变成空对象。
3.如果obj里有函数、undefined,则序列化的结果会把函数或undefined丢失。
4.如果obj里有NaN、Infinity和-Infinity,则序列化的结果会变成null
5.JSON.stringify只能序列化可枚举的自由属性,例如:如果obj中的对象是由构造函数生成的,则实行深拷贝后,会丢失对象的constructor;
6.如果对象中存在循环引用的情况也无法正确实现深拷贝
利用递归的方式实现深拷贝
//使用递归的方式实现数组、对象的深拷贝
function deepClone1(obj) {
//判断拷贝的要进行深拷贝的是数组还是对象,是数组的话进行数组拷贝,对象的话进行对象拷贝
var objClone = Array.isArray(obj) ? [] : {};
//进行深拷贝的不能为空,并且是对象或者是
if (obj && typeof obj === "object") {
for (key in obj) {
if (obj.hasOwnProperty(key)) {
if (obj[key] && typeof obj[key] === "object") {
objClone[key] = deepClone1(obj[key]);
} else {
objClone[key] = obj[key];
}
}
}
}
return objClone;
}
利用Jquery的extend方式实现深拷贝
var $ = require('jquery');
var newObj = $.extend(true, {}, obj);
通过loadsh.CloneDeep()方法实现拷贝
var _ = require('lodash');
var newObj = _.cloneDeep(obj);
通过Object.assign()拷贝
function assignDeep(target, ...sources) {
// 1. 参数校验
if (target == null) {
throw new TypeError('Cannot convert undefined or null to object');
}
// 2. 如果是基本类型数据转为包装对象
let result = Object(target);
// 3. 缓存已拷贝过的对象,解决引用关系丢失问题
if (!result['__hash__']) {
result['__hash__'] = new WeakMap();
}
let hash = result['__hash__'];
sources.forEach(v => {
// 4. 如果是基本类型数据转为对象类型
let source = Object(v);
// 5. 遍历原对象属性,基本类型则值拷贝,对象类型则递归遍历
Reflect.ownKeys(source).forEach(key => {
// 6. 跳过自有的不可枚举的属性
if (!Object.getOwnPropertyDescriptor(source, key).enumerable) {
return;
}
if (typeof source[key] === 'object' && source[key] !== null) {
// 7. 属性的冲突处理和拷贝处理
let isPropertyDone = false;
if (!result[key] || !(typeof result[key] === 'object')
|| Array.isArray(result[key]) !== Array.isArray(source[key])) {
// 当 target 没有该属性,或者属性类型和 source 不一致时,直接整个覆盖
if (hash.get(source[key])) {
result[key] = hash.get(source[key]);
isPropertyDone = true;
} else {
result[key] = Array.isArray(source[key]) ? [] : {};
hash.set(source[key], result[key]);
}
}
if (!isPropertyDone) {
result[key]['__hash__'] = hash;
assignDeep(result[key], source[key]);
}
} else {
Object.assign(result, {[key]: source[key]});
}
});
});
delete result['__hash__'];
return result;
}