在面试中你是否遇到过以下场景:前端
Q:小朋友,你是否了解如何拷贝一个对象?面试
R:此时,机智的你可能会想到缓存
Object.assign({}, obj);
Q:那如何深拷贝一个对象呢?闭包
R:机智的你函数
JSON.parse(JSON.stringify(obj));
Q:使用stringify
这种方式有何弊端?post
性能问题,stringify
再解析其实须要耗费较多时间,特别是数据量大的时候。性能
一些类型没法拷贝,例如函数(不输出),正则(输出空对象),时间对象(输出时间字符串),Undefiend(不输出)学习
遇到循环引用的对象会出错prototype
同层(非同层)同引用的问题,理论下两个key
对应的val
若是指向同一个对象,拷贝也应该指向一个相同新地址才对code
Q:那你能本身实现个深拷贝函数?
R:以下:
const deepClone = (obj) => { // 非引用类型及函数将直接返回 if (!obj || typeof obj !== 'object') return obj; // 特殊的引用类型处理 switch(Object.prototype.toString.call(obj).slice(8, -1)) { case 'Date': return new Date(obj); break; case 'RegExp': return new RegExp(obj); break; case 'String': return new String(obj); break; case 'Number': return new Number(obj); break; case 'Boolean': return new Boolean(obj); break; } const result = obj instanceof Array ? [] : {}; for (let propName in obj) { if (obj.hasOwnProperty(propName)) { result[propName] = deepClone(obj[propName]); } } return result; }
优势:实现了大多数数据类型的拷贝,全部非引用类型及引用类型的String Number Boolean Function Array Date RegExp
缺点:未考虑一些特殊的引用类型如Error Math Symbol Map Set JSON
,函数属于引用拷贝,未解决循环引用的问题
Q:如何解决循环引用?
R:将父层级的数据缓存对比(能够顺带解决同层(非同层)同引用的问题)
const deepClone = (obj) => { // 非引用类型及函数将直接返回 if (!obj || typeof obj !== 'object') return obj; // 特殊的引用类型处理 switch (Object.prototype.toString.call(obj).slice(8, -1)) { case 'Date': return new Date(obj); break; case 'RegExp': return new RegExp(obj); break; case 'String': return new String(obj); break; case 'Number': return new Number(obj); break; case 'Boolean': return new Boolean(obj); break; } const map = deepClone.map = deepClone.map || new Map(); // 使用map结构能够没必要循环缓存,提升效率 if (map.get(obj)) { return map.get(obj); } const result = obj instanceof Array ? [] : {}; // 若是仔细观察能够发现解决了同层同引用的问题 map.set(obj, result); for (let propName in obj) { if (obj.hasOwnProperty(propName)) { result[propName] = deepClone(obj[propName]); } } return result; }
Q:为何函数仍是指向原来的函数,而不建立新函数?
R:理论下函数也能够经过new Function(code)
来建立新的函数,可是若是遇到闭包函数,咱们没法获得原函数的外层定义的变量及其原有做用域链,这些在JS词法解析时完成的步骤咱们没法得知,全部只能引用原函数比较好。
Sum: 上面实现的缺点主要是没有彻底覆盖特殊引用类型,但其实咱们平时应该不会遇到那些类型,因此能够凑合使用。若是还有其它的问题没有考虑到或者有出错的,但愿你们能够帮忙指出。
欢迎到前端学习打卡群一块儿学习~516913974