一、简介
浅拷贝是拷贝一层,如果数据是基本数据类型,会拷贝其本身,如果除了基本数据类型之外还有一层对象,那么只能拷贝其引用,对象的改变会反应到拷贝对象上。
深拷贝是拷贝多层,每一层的数据都会拷贝出来,对象的改变不会影响拷贝对象。
二、实现
1、实现浅拷贝
1)数组自带的浅拷贝方法:slice()、concat、Array.from()、... 操作符
let arr = [1, 2, 3];
let arr1 = arr;
//以下4种方式实现数组浅拷贝
let arr2 = arr.slice();
let arr2 = [...arr]
let arr2 = [].concat(arr);
let arr2 = Array.from(arr)
arr[0] = 4;
console.log(arr, arr1, arr2)
通过上图我们可以看到,arr1不通过浅拷贝直接赋值, 此时arr1复制的是arr的引用,arr值改变arr1也会改变,而浅拷贝则不会。
2)对象自带的浅拷贝方法:... 操作符、Object.assign()
let obj = {
a: 1,
b: 2
}
let obj1 = obj;
//以下是对象两种自带的浅拷贝方法
let obj2 = Object.assign({}, obj)
let { ...obj2 } = obj;
obj.a = 3;
console.log(obj, obj1, obj2)
通过上图我们可以看到,obj1不通过浅拷贝直接赋值, 此时obj1复制的是obj的引用,obj值改变obj1也会改变,而浅拷贝则不会。
2.实现深拷贝
1)通过JSON.parse(JSON.stringify(obj))
这个方法简单,平常项目也常拿来使用。缺点:会忽略undefined、任意的函数、正则、symbol 值等;
Date类型会转为字符串
2)简单实现深拷贝方法
function deepClone(obj){
let objClone = Array.isArray(obj)?[]:{};
if(obj && typeof obj==="object"){
for(key in obj){
if(obj.hasOwnProperty(key)){
//判断ojb子元素是否为对象,如果是,递归复制
if(obj[key]&&typeof obj[key] ==="object"){
objClone[key] = deepClone(obj[key]);
}else{
//如果不是,简单复制
objClone[key] = obj[key];
}
}
}
}
return objClone;
}
目前只考虑简单的对象和数组两种情况,没有考虑日期,正则等等其他情况。主要是,当对象的子项还是对象类型时,递归调用。
考虑更多情况深拷贝实现
function isType(obj, type) {
if (typeof obj !== 'object') return false;
return Object.prototype.toString.call(obj).replace('[object ', '').replace(']', '') === type
}
const getRegExp = re => {
var flags = '';
if (re.global) flags += 'g';
if (re.ignoreCase) flags += 'i';
if (re.multiline) flags += 'm';
return flags;
};
const clone = parent => {
// 维护两个储存循环引用的数组
const parents = [];
const children = [];
const _clone = parent => {
if (parent === null) return null;
if (typeof parent !== 'object') return parent;
let child, proto;
if (isType(parent, 'Array')) {
// 对数组做特殊处理
child = [];
} else if (isType(parent, 'RegExp')) {
// 对正则对象做特殊处理
child = new RegExp(parent.source, getRegExp(parent));
if (parent.lastIndex) child.lastIndex = parent.lastIndex;
} else if (isType(parent, 'Date')) {
// 对Date对象做特殊处理
child = new Date(parent.getTime());
} else {
// 处理对象原型
proto = Object.getPrototypeOf(parent);
// 利用Object.create切断原型链
child = Object.create(proto);
}
// 处理循环引用
const index = parents.indexOf(parent);
if (index != -1) {
// 如果父数组存在本对象,说明之前已经被引用过,直接返回此对象
return children[index];
}
parents.push(parent);
children.push(child);
for (let i in parent) {
// 递归
child[i] = _clone(parent[i]);
}
return child;
};
return _clone(parent);
};
function person(pname) {
this.name = pname;
}
const Messi = new person('Messi');
function say() {
console.log('hi');
}
const oldObj = {
a: say,
c: new RegExp('ab+c', 'i'),
d: Messi,
};
oldObj.b = oldObj;
const newObj = clone(oldObj);
console.log(newObj)
3.有些第三方库也封装好了深拷贝的方法
如 loadash的cloneDeep方法
深拷贝的也可参考这篇文章