手写深拷贝
深拷贝
深拷贝简单理解就是b是a的一份拷贝,且b中不存在a中对象的引用
深拷贝的实现
1.JSON序列化和反序列化
如果对象中全是基本类型,那么可以使用JSON.parse(JSON.stringify(a))
2.递归克隆
2.1 如果是普通类型,就直接拷贝
测试用例:
describe('deepClone', () => {
it('能够复制基本类型number,string,boolean,undefined,null,symbol', () => {
const n = 123;
const n2 = deepClone(n);
assert(n === n2);
const s = '12345';
const s2 = deepClone(s);
assert(s === s2);
const b = true;
const b2 = deepClone(b);
assert(b === b2);
const u = undefined;
const u2 = deepClone(u);
assert(u === u2);
const empty = null;
const empty2 = deepClone(empty);
assert(empty === empty2);
const sym = Symbol();
const sym2 = deepClone(sym);
assert(sym === sym2);
});
});
代码实现:
function deepClone(source) {
return source;
}
此时我们能够实现基本类型的拷贝
2.2 如果是对象
2.2.1 普通对象
使用for in 遍历对象上的属性,返回一个新的对象(注: for in 会遍历原型上的属性)
测试用例:
it('能够复制对象', () => {
const obj1 = { name: 'sss', child: { name: 'sss-children' } };
const obj2 = deepClone(obj1);
assert(obj1 !== obj2);
assert(obj1.name === obj2.name);
assert(obj1.child !== obj2.child);
assert(obj1.child.name === obj2.child.name);
});
代码实现:
function deepClone(source) {
if (source instanceof Object) {
let dist = new Object();
for (let key in source) {
// for in会遍历原型上的属性
if (source.hasOwnProperty(key)) {
dist[key] = deepClone(source[key]);
}
}
return dist;
}
return source;
}
2.2.2 数组对象
使用new Array初始化
测试用例:
it('能够复制数组对象', () => {
const array1 = [
[11, 12],
[21, 22],
[31, 32],
];
const array2 = deepClone(array1);
assert(array1 !== array2);
assert(array1[0] !== array2[0]);
assert(array1[1] !== array2[1]);
assert(array1[2] !== array2[2]);
assert.deepEqual(array1, array2);
});
代码实现:
function deepClone(source) {
if (source instanceof Object) {
let dist;
if (source instanceof Array) {
dist = new Array();
} else {
dist = new Object();
}
for (let key in source) {
// for in会遍历原型上的属性
if (source.hasOwnProperty(key)) {
dist[key] = deepClone(source[key]);
}
}
return dist;
}
return source;
}
2.2.3 函数对象
返回一个新的函数,使用apply(this,arguments)
测试用例:
it('能够复制函数', () => {
const f1 = function (x, y) {
return x + y;
};
f1.xxx = { yyy: { zzz: 1 } };
const f2 = deepClone(f1);
assert(f1 !== f2);
assert(f1.xxx !== f2.xxx);
assert(f1.xxx.yyy !== f2.xxx.yyy);
assert(f1.xxx.yyy.zzz === f2.xxx.yyy.zzz);
assert(f1(1, 2) === f2(1, 2));
});
代码实现:
function deepClone(source) {
if (source instanceof Object) {
let dist;
if (source instanceof Array) {
dist = new Array();
} else if (source instanceof Function) {
dist = function () {
return source.apply(this, arguments);
};
} else {
dist = new Object();
}
for (let key in source) {
// for in会遍历原型上的属性
if (source.hasOwnProperty(key)) {
dist[key] = deepClone(source[key]);
}
}
return dist;
}
return source;
}
2.2.4 日期和正则
日期使用new Date创建一个新的日期对象,正则使用new RegExp(source.source, source.flags)
测试用例:
it('可以复制正则', () => {
const reg1 = /hi\d+/gi;
reg1.xxx = { yyy: { zzz: 1 } };
const reg2 = deepClone(reg1);
assert(reg1.source === reg2.source);
assert(reg1.flags === reg2.flags);
assert(reg1 !== reg2);
assert(reg1.xxx !== reg2.xxx);
assert(reg1.xxx.yyy !== reg2.xxx.yyy);
assert(reg1.xxx.yyy.zzz === reg2.xxx.yyy.zzz);
});
it('可以复制日期', () => {
const date1 = new Date();
date1.xxx = { yyy: { zzz: 1 } };
const date2 = deepClone(date1);
assert(date1.source === date2.source);
assert(date1.flags === date2.flags);
assert(date1 !== date2);
assert(date1.getTime() === date2.getTime());
assert(date1.xxx !== date2.xxx);
assert(date1.xxx.yyy !== date2.xxx.yyy);
assert(date1.xxx.yyy.zzz === date2.xxx.yyy.zzz);
});
代码实现
function deepClone(source) {
if (source instanceof Object) {
let dist;
if (source instanceof Array) {
dist = new Array();
} else if (source instanceof Function) {
dist = function () {
return source.apply(this, arguments);
};
} else if (source instanceof RegExp) {
dist = new RegExp(source.source, source.flags);
} else if (source instanceof Date) {
dist = new Date(source);
} else {
dist = new Object();
}
for (let key in source) {
// for in会遍历原型上的属性
if (source.hasOwnProperty(key)) {
dist[key] = deepClone(source[key]);
}
}
return dist;
}
return source;
}
目前我们粗略的实现了一个可以复制基本类型和常见对象的深拷贝
2.3 考虑环
const a = {}
a.self = a
测试用例:
it('环也能复制', () => {
const obj1 = { name: 'sss' };
obj1.self = obj1;
const obj2 = deepClone(obj1);
assert(obj1 !== obj2);
assert(obj1.name === obj2.name);
assert(obj1.self !== obj2.self);
});
此时我们直接运行之前的代码,发现控制台直接报错了。因为对象每次深拷贝之后会生成一个全新的对象,所以此时会造成递归不能终止。因此我们需要先判断是否存在环,然后进行对象的拷贝。
我们创建一个cashe数组,里面存放origin和new
let cache = [];
function findCache(source) {
for (let i = 0; i < cache.length; i++) {
if (cache[i].origin && cache[i].origin === source) {
return cache[i].new;
}
}
return undefined;
}
function deepClone(source) {
if (source instanceof Object) {
let cachedDist = findCache(source);
if (cachedDist) {
return cachedDist;
} else {
let dist;
if (source instanceof Array) {
dist = new Array();
} else if (source instanceof RegExp) {
dist = new RegExp(source.source, source.flags);
} else if (source instanceof Function) {
dist = function () {
return source.apply(this, arguments);
};
} else if (source instanceof Date) {
dist = new Date(source);
} else {
dist = new Object();
}
cache.push({ origin: source, new: dist });
for (let key in source) {
// for in会遍历原型上的属性
if (source.hasOwnProperty(key)) {
dist[key] = deepClone(source[key]);
}
}
return dist;
}
}
return source;
}