当遇到一个无限层级的对象拷贝时,我们的第一反应应该就是递归
下面,我们仅考虑对象
和基本类型
来做一个深拷贝:
function getType(val) {
return Object.prototype.toString.call(val);
}
function deepClone(obj) {
if (getType(obj) === '[object Object]') {
const newObj = {};
for (const key in obj) {
// 过滤原型上的属性
if (obj.hasOwnProperty(key)) {
newObj[key] = deepClone(obj[key]);
}
}
return newObj;
} else {
return obj;
}
}
在确定了深拷贝的基本逻辑后,接下来,我们对数组进行兼容
function getType(val) {
return Object.prototype.toString.call(val);
}
function deepClone(obj) {
if (getType(obj) === '[object Object]') {
const newObj = Array.isArray(obj) ? [] : {}; // 这里对类型进行判断,返回空数组或空对象
for (const key in obj) {
// 过滤原型上的属性
if (obj.hasOwnProperty(key)) {
newObj[key] = deepClone(obj[key]);
}
}
return newObj;
} else {
return obj;
}
}
正则表达式可以通过属性source
和flags
获取其正则的内容和参数,如下
const reg = /(?<=\[)(.*)(?=\])/g;
console.log(reg.source); // 输出(?<=\[)(.*)(?=\])
console.log(reg.flags); // 输出g
获取到正则的主体和参数后,我们就可以来构造一个正则表达式了
代码如下:
function getType(val) {
return Object.prototype.toString.call(val);
}
function deepClone(item) {
if (item instanceof Object) {
const type = getType(item);
switch (type) {
case '[object RegExp]':
return new RegExp(item.source, item.flags); // 取出正则表达式中的内容和参数,重新赋值
case '[object Object]':
case '[object Array]':
const newItem = Array.isArray(item) ? [] : {};
for (const key in item) {
// 过滤原型上的属性
if (item.hasOwnProperty(key)) {
newItem[key] = deepClone(item[key]);
}
}
return newItem;
// 如函数/日期等对象,直接返回
default:
return item;
}
} else {
return item;
}
}
function getType(val) {
return Object.prototype.toString.call(val);
}
function deepClone(item) {
if (item instanceof Object) {
const type = getType(item);
switch (type) {
case '[object RegExp]':
return new RegExp(item.source, item.flags); // 取出正则表达式中的内容和参数,重新赋值
case '[object Function]':
return new Function('return ' + item.toString())(); // 使用new Function()实例化一个新的函数
case '[object Object]':
case '[object Array]':
const newItem = Array.isArray(item) ? [] : {};
for (const key in item) {
// 过滤原型上的属性
if (item.hasOwnProperty(key)) {
newItem[key] = deepClone(item[key]);
}
}
return newItem;
// 如函数/日期等对象,直接返回
default:
return item;
}
} else {
return item;
}
}
由于我们是使用递归实现的深拷贝,当存在一个属性指向自身的时候,会导致无限递归,栈溢出的情况。如下示例:
const obj = {
name: 'kerwin',
};
obj.temp = obj; // obj的某个属性指向了本身
const newObj = deepClone(obj); // 报错:栈溢出
为解决上述的情况,我们需要额外添加一种递归的终止条件,即:当某对象已经被拷贝过,则不再进行递归。
通过一个哈希表来存储已拷贝过的对象,当递归过程中发现哈希表中已存在该指,则直接取出,不再递归
function deepClone(item, cache = new Map()) {
// 判断哈希表中是否存在当前对象的索引
if (cache.has(item)) {
return cache.get(item);
}
if (item instanceof Object) {
const type = getType(item);
switch (type) {
case '[object RegExp]':
return new RegExp(item.source, item.flags); // 取出正则表达式中的内容和参数,重新赋值
case '[object Function]':
return new Function('return ' + item.toString())();
case '[object Object]':
case '[object Array]':
const newItem = Array.isArray(item) ? [] : {};
cache.set(item, newItem); // 将当前对象存储到哈希表中
for (const key in item) {
// 过滤原型上的属性
if (item.hasOwnProperty(key)) {
newItem[key] = deepClone(item[key], cache);
}
}
return newItem;
// 如函数/日期等对象,直接返回
default:
return item;
}
} else {
return item;
}
}