题目:
请补全JavaScript代码,要求实现对象参数的深拷贝并返回拷贝之后的新对象。
题目难度:中等
注意:
- 需要考虑函数、正则、日期、ES6新对象
- 需要考虑循环引用问题
题解
本题考点:递归、遍历、Map
根据题目要求,实现对象参数的深拷贝并返回拷贝之后的新对象,因为需要考虑参数对象和参数对象的每个数据项的数据类型可能包括函数、正则、日期、ES6新对象且必须考虑循环引用问题,所以需要引入ES6新对象Map并且详细的判断数据类型,核心步骤有:
- 首先判断对象参数是否为“null”,是则返回“null”
- 判断对象参数数据类型是否为“object”,不是则返回该参数
- 获取到对象参数的构造函数名,判断是否为函数、正则、日期、ES6新对象其中之一,如果是则直接返回通过该参数对象对应的构造函数生成的新实例对象
- 当以上条件判断之后函数依然没有结束时继续进行以下操作
- 在Map对象中获取当前参数对象,如果能获取到,则说明这里为循环引用并返回Map对象中该参数对象的值
- 如果在Map对象中没有获取到对应的值,则保存该参数对象到Map中,作为标记
- 根据该参数的数据类型是否为数组创建新对象
- 遍历该对象参数,将每一项递归调用该函数本身的返回值赋给新对象 参考答案
const _completeDeepClone = (target, map = new Map()) => {
if(target === null) return target
if(typeof target !== 'object') return target
const constructor = target.constructor
if(/^(Function|RegExp|Date|Map|Set)$/i.test(constructor.name)) return new constructor(target)
if(map.get(target)) return map.get(target)
map.set(target, true)
const cloneTarget = Array.isArray(target) ? [] : {}
for(prop in target) {
if(target.hasOwnProperty(prop)) {
cloneTarget[prop] = _completeDeepClone(target[prop], map)
}
}
return cloneTarget
}
附:
赋值,浅拷贝,深拷贝的区别
| 和原始数据是否指向同一对象 | 第一层数据为基本数据类型 | 原数据中包含子对象 | |
|---|---|---|---|
| 赋值 | 是 | 改变会使原数据一起改变 | 改变会使原数据一起改变 |
| 浅拷贝 | 否 | 改变不会使原数据一起改变 | 改变会使原数据一起改变 |
| 深拷贝 | 否 | 改变不会使原数据一起改变 | 改变不会使原数据一起改变 |
实现深拷贝的几种方式(先介绍两种常用的)
1、JSON转换
var targetObj = JSON.parse(JSON.stringify(copyObj))
let arr4 = JSON.parse(JSON.stringify(arr))
缺点:
- 如果对象里有函数,函数无法被拷贝下来;
- 无法拷贝对象原型链上的属性和方法;
- 当数据的层级很深,会导致栈溢出。
2、普通递归函数
function deepCopy( source ) {
if (!isObject(source)) return source; //如果不是对象的话直接返回
let target = Array.isArray( source ) ? [] : {} //数组兼容
for ( var k in source ) {
if (source.hasOwnProperty(k)) {
if ( typeof source[ k ] === 'object' ) {
target[ k ] = deepCopy( source[ k ] )
} else {
target[ k ] = source[ k ]
}
}
}
return target
}
function isObject(obj) {
return typeof obj === 'object' && obj !== null
}
缺点:
- 无法保持引用;
- 当数据的层级很深,会导致栈溢出。
还有其他实现深拷贝的方式哦,快来评论区告诉我(* ̄︶ ̄)