基于该大神的博客,自己做了一些总结
- 利用JSON.parse、JSON.stringify()
const oldObj = {
a: 1,
b: [ 'e', 'f', 'g' ],
c: { h: { i: 2 } }
};
const newObj = JSON.parse(JSON.stringify(oldObj));
console.log(newObj.c.h, oldObj.c.h); // { i: 2 } { i: 2 }
console.log(oldObj.c.h === newObj.c.h); // false
newObj.c.h.i = 'change';
console.log(newObj.c.h, oldObj.c.h); // { i: 'change' } { i: 2 }
复制代码
- 该方法存在的问题有:
- 无法实现对函数、RegExp等特殊对象的克隆(会在JSON.stringify阶段被直接忽略)
- 会抛弃对象的constructor,所有构造函数会指向Object
- 对象有循环引用,会报错
- 因为一些特殊情况的对象,如正则、数组、Date等,所以以上深克隆方法不可取。我们需要实现一个对象类型判断函数
const isType = (obj, type) => {
if (typeof obj !== 'object') return false;
const typeString = Object.prototype.toString.call(obj);
let flag;
switch (type) {
case 'Array':
flag = typeString === '[object Array]';
break;
case 'Date':
flag = typeString === '[object Date]';
break;
case 'RegExp':
flag = typeString === '[object RegExp]';
break;
case 'Function':
flag = typeString === '[object Function]';
break;
default:
flag = false;
}
return flag;
};
const arr = Array.of(3, 4, 5, 2);
console.log(isType(arr, 'Array')); // true
复制代码
- 针对正则表达式,我们还要提取flags属性
const getRegExp = re =>{
var flags = '';
if(re.global) flag += 'g';
if(re.ignoreCase) flags += 'i';
if(re.multiline) flags += 'm';
return flags;
}
复制代码
-
做好了这些准备工作,我们就可以进行深克隆的实现了
- 实现思路:递归
- 一定要把引用类型区分开:object,function,Date,RegExp,Array。根据这些类型,做出不同的克隆操作
- 循环引用最特殊,可能被克隆的对象里不止一次循环引用,需要借助额外的数组专门记录,且该数组脱离于递归之外,不能被递归影响
var parents = [];//专门用来记录parent里出现循环引用的位置 var children = [];//记录已被克隆的内容 var 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,'Function')){ child = parent; return child; }else if(isType(parent,'Date')){ child = new Date(parent.getTime()); return child; }else if(isType(parent,'RegExp')){ child = new Date(parent.source,getRegExp(parent)); return child; }else{ proto = Object.getPrototypeOf(parent); child = Object.create(proto); } //处理循环引用、递归 let index = parents.indexOf(parent); if(index != -1){ //递归过程中发现循环引用,这个引用一定是之前递归里已经克隆的内容,存在了children中 return children[index]; } parents.push(parent); children.push(child); for(let i in parents){ //let in语法,对数组、对象都适用 child[i] = clone(parent[i]); } return child } 复制代码