FED15 浅拷贝

描述

请补全JavaScript代码,要求实现一个对象参数的浅拷贝并返回拷贝之后的新对象。
注意:1. 参数可能包含函数、正则、日期、ES6新对象

本题考点:遍历
根据题目要求,实现一个对象参数的浅拷贝并返回拷贝之后的新对象,因为可能包含函数、正则、日期、ES6新对象,所以需要对这些对象类型进行特殊判断,核心步骤有:

  1. 如果对象参数的数据类型不为”object”或为”null”,则直接返回该参数
  2. 如果是”object”,就获取该参数的构造函数名,通过正则表达式判断该对象是否为函数、正则、日期、ES6新对象等,如果返回true,则直接返回该参数
  3. 当以上条件判断之后函数依然没有结束时,此时通过数组的原型方法判断该参数为普通对象或数组并创建相应数据类型的新变量
  4. 进入遍历体,将对象参数的每一项赋值给新变量
  5. 最终返回该新变量

参考答案:

  1. const _shallowClone = target => {
  2. if(typeof target === 'object' && target !== null) {
  3. const constructor = target.constructor
  4. // 正则匹配判断
  5. if(/^(Function|RegExp|Date|Map|Set)$/i.test(constructor.name)) return target
  6. const cloneTarget = Array.isArray(target) ? [] : {}
  7. for(key in target) {
  8. if(target.hasOwnProperty(key)) {
  9. cloneTarget[key] = target[key]
  10. }
  11. }
  12. return cloneTarget
  13. } else {
  14. return target
  15. }
  16. }

FED16 简易深拷贝

描述

请补全JavaScript代码,要求实现对象参数的深拷贝并返回拷贝之后的新对象。
注意:
1. 参数对象和参数对象的每个数据项的数据类型范围仅在数组、普通对象({})、基本数据类型中]
2. 无需考虑循环引用问题

参考答案:

  1. const _sampleDeepClone = target => {
  2. // base case
  3. if (target == null || typeof target !== 'object') {
  4. return target;
  5. }
  6. const cloneTarget = Array.isArray(target) ? [] : {};
  7. for (let key in target) {
  8. if (target.hasOwnProperty(key)) {
  9. if (target[key] && typeof target[key] == 'object') {
  10. // 递归
  11. cloneTarget[key] = _sampleDeepClone(target[key]);
  12. } else {
  13. // 基本数据类型
  14. cloneTarget[key] = target[key];
  15. }
  16. }
  17. }
  18. return cloneTarget;
  19. };


FED17 深拷贝l378438c19896e5988d4916885552d8ff-s-m6901fbc14f1b010a9dbff83a2f75d800.jpg

描述

请补全JavaScript代码,要求实现对象参数的深拷贝并返回拷贝之后的新对象。
注意:
1. 需要考虑函数、正则、日期、ES6新对象
2. 需要考虑循环引用问题

本题考点:递归、遍历、Map
根据题目要求,实现对象参数的深拷贝并返回拷贝之后的新对象,因为需要考虑参数对象和参数对象的每个数据项的数据类型可能包括函数、正则、日期、ES6新对象且必须考虑循环引用问题,所以需要引入ES6新对象Map并且详细的判断数据类型,核心步骤有:

  1. 首先判断对象参数是否为“null”,是则返回“null”
  2. 判断对象参数数据类型是否为“object”,不是则返回该参数
  3. 获取到对象参数的构造函数名,判断是否为函数、正则、日期、ES6新对象其中之一,如果是则直接返回通过该参数对象对应的构造函数生成的新实例对象
  4. 当以上条件判断之后函数依然没有结束时继续进行以下操作
  5. 在Map对象中获取当前参数对象,如果能获取到,则说明这里为循环引用并返回Map对象中该参数对象的值
  6. 如果在Map对象中没有获取到对应的值,则保存该参数对象到Map中,作为标记
  7. 根据该参数的数据类型是否为数组创建新对象
  8. 遍历该对象参数,将每一项递归调用该函数本身的返回值赋给新

参考答案:

  1. const _completeDeepClone = (target, map = new Map()) => {
  2. if(target === null) return target
  3. if(typeof target !== 'object') return target
  4. const constructor = target.constructor
  5. if(/^(Function|RegExp|Date|Map|Set)$/i.test(constructor.name)) {
  6. return new constructor(target)
  7. }
  8. if(map.get(target)) return map.get(target)
  9. map.set(target, true)
  10. const cloneTarget = Array.isArray(target) ? [] : {}
  11. for(prop in target) {
  12. if(target.hasOwnProperty(prop)) {
  13. cloneTarget[prop] = _completeDeepClone(target[prop], map)
  14. }
  15. }
  16. return cloneTarget
  17. }

深拷贝之循环引用处理

循环引用

解决循环引用问题,我们可以额外开辟一个存储空间,来存储当前对象和拷贝对象的对应关系
这个存储空间,需要可以存储key-value形式的数据,且key可以是一个引用类型,我们可以选择 WeakMap 这种数据结构:

  • 检查 WeakMap 中有无克隆过的对象
  • 有 - 直接返回
  • 没有 - 将当前对象作为key,克隆对象作为value进行存储
  • 继续克隆

    1. function cloneDeep(source, hash = new WeakMap()) {
    2. if (!isObject(source)) return source;
    3. if (hash.has(source)) return hash.get(source); // 新增代码,查哈希表
    4. var target = Array.isArray(source) ? [] : {};
    5. hash.set(source, target); // 新增代码,哈希表设值
    6. for(var key in source) {
    7. if (Object.prototype.hasOwnProperty.call(source, key)) {
    8. if (isObject(source[key])) {
    9. target[key] = cloneDeep(source[key], hash); // 新增代码,传入哈希表
    10. } else {
    11. target[key] = source[key];
    12. }
    13. }
    14. }
    15. return target;
    16. }