js-深克隆的实现方法

基于该大神的博客,自己做了一些总结

请你实现一个深克隆

  1. 利用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 }
复制代码
  • 该方法存在的问题有:
    1. 无法实现对函数、RegExp等特殊对象的克隆(会在JSON.stringify阶段被直接忽略)
    2. 会抛弃对象的constructor,所有构造函数会指向Object
    3. 对象有循环引用,会报错
  1. 因为一些特殊情况的对象,如正则、数组、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;
}
复制代码
  • 做好了这些准备工作,我们就可以进行深克隆的实现了

    1. 实现思路:递归
    2. 一定要把引用类型区分开:object,function,Date,RegExp,Array。根据这些类型,做出不同的克隆操作
    3. 循环引用最特殊,可能被克隆的对象里不止一次循环引用,需要借助额外的数组专门记录,且该数组脱离于递归之外,不能被递归影响
    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
        
    }
    复制代码

转载于:https://juejin.im/post/5d04b716f265da1bbd4b7348

猜你喜欢

转载自blog.csdn.net/weixin_34326558/article/details/93181568
今日推荐