乘风原创程序

  • JS面试题中深拷贝的实现讲解
  • 2020/5/13 9:09:20
  • 在面试中你是否遇到过如下场景:

    q:小朋友,你是否了解如何拷贝一个对象?

    r:此时,机智的你可能会想到

    object.assign({}, obj);

    q:那如何深拷贝一个对象呢?

    r:机智的你

    json.parse(json.stringify(obj));

    q:使用stringify这种方式有何弊端?

    1. 性能问题,stringify再解析其实需要耗费较多时间,特别是数据量大的时候。
    2. 一些类型无法拷贝,例如函数(不输出),正则(输出空对象),时间对象(输出时间字符串),undefiend(不输出)
    3. 遇到循环引用的对象会出错
    4. 同层(非同层)同引用的问题,理论下两个key对应的val如果指向同一个对象,拷贝也应该指向一个相同新地址才对

    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: 上面实现的缺点主要是没有完全覆盖特殊引用类型,但其实我们平时应该不会遇到那些类型,所以可以凑合使用。如果还有其它的问题没有考虑到或者有出错的,希望大家可以帮忙指出。

    以上就是js面试题中深拷贝的实现讲解的详细内容,更多关于js深拷贝的实现的资料请关注本教程网其它相关文章!