手写深拷贝

手写深拷贝

深拷贝

深拷贝简单理解就是b是a的一份拷贝,且b中不存在a中对象的引用

深拷贝的实现

1.JSON序列化和反序列化

如果对象中全是基本类型,那么可以使用JSON.parse(JSON.stringify(a))

2.递归克隆

2.1 如果是普通类型,就直接拷贝

测试用例:

describe('deepClone', () => {
  it('能够复制基本类型number,string,boolean,undefined,null,symbol', () => {
    const n = 123;
    const n2 = deepClone(n);
    assert(n === n2);
    const s = '12345';
    const s2 = deepClone(s);
    assert(s === s2);
    const b = true;
    const b2 = deepClone(b);
    assert(b === b2);
    const u = undefined;
    const u2 = deepClone(u);
    assert(u === u2);
    const empty = null;
    const empty2 = deepClone(empty);
    assert(empty === empty2);
    const sym = Symbol();
    const sym2 = deepClone(sym);
    assert(sym === sym2);
  });
});

代码实现:

function deepClone(source) {
  return source;
}
image.png

此时我们能够实现基本类型的拷贝

2.2 如果是对象

2.2.1 普通对象

使用for in 遍历对象上的属性,返回一个新的对象(注: for in 会遍历原型上的属性)
测试用例:

it('能够复制对象', () => {
      const obj1 = { name: 'sss', child: { name: 'sss-children' } };
      const obj2 = deepClone(obj1);
      assert(obj1 !== obj2);
      assert(obj1.name === obj2.name);
      assert(obj1.child !== obj2.child);
      assert(obj1.child.name === obj2.child.name);
    });

代码实现:

function deepClone(source) {
  if (source instanceof Object) {
    let dist = new Object();
    for (let key in source) {
      // for in会遍历原型上的属性
      if (source.hasOwnProperty(key)) {
        dist[key] = deepClone(source[key]);
      }
    }

    return dist;
  }

  return source;
}
image.png
2.2.2 数组对象

使用new Array初始化

测试用例:

it('能够复制数组对象', () => {
      const array1 = [
        [11, 12],
        [21, 22],
        [31, 32],
      ];
      const array2 = deepClone(array1);
      assert(array1 !== array2);
      assert(array1[0] !== array2[0]);
      assert(array1[1] !== array2[1]);
      assert(array1[2] !== array2[2]);
      assert.deepEqual(array1, array2);
    });

代码实现:

function deepClone(source) {
  if (source instanceof Object) {
    let dist;
    if (source instanceof Array) {
      dist = new Array();
    } else {
      dist = new Object();
    }

    for (let key in source) {
      // for in会遍历原型上的属性
      if (source.hasOwnProperty(key)) {
        dist[key] = deepClone(source[key]);
      }
    }

    return dist;
  }

  return source;
}
2.2.3 函数对象

返回一个新的函数,使用apply(this,arguments)

测试用例:

 it('能够复制函数', () => {
      const f1 = function (x, y) {
        return x + y;
      };
      f1.xxx = { yyy: { zzz: 1 } };
      const f2 = deepClone(f1);
      assert(f1 !== f2);
      assert(f1.xxx !== f2.xxx);
      assert(f1.xxx.yyy !== f2.xxx.yyy);
      assert(f1.xxx.yyy.zzz === f2.xxx.yyy.zzz);
      assert(f1(1, 2) === f2(1, 2));
    });

代码实现:

function deepClone(source) {
  if (source instanceof Object) {
    let dist;
    if (source instanceof Array) {
      dist = new Array();
    } else if (source instanceof Function) {
      dist = function () {
        return source.apply(this, arguments);
      };
    } else {
      dist = new Object();
    }
    for (let key in source) {
      // for in会遍历原型上的属性
      if (source.hasOwnProperty(key)) {
        dist[key] = deepClone(source[key]);
      }
    }

    return dist;
  }

  return source;
}
2.2.4 日期和正则

日期使用new Date创建一个新的日期对象,正则使用new RegExp(source.source, source.flags)

测试用例:

 it('可以复制正则', () => {
      const reg1 = /hi\d+/gi;
      reg1.xxx = { yyy: { zzz: 1 } };
      const reg2 = deepClone(reg1);
      assert(reg1.source === reg2.source);
      assert(reg1.flags === reg2.flags);
      assert(reg1 !== reg2);
      assert(reg1.xxx !== reg2.xxx);
      assert(reg1.xxx.yyy !== reg2.xxx.yyy);
      assert(reg1.xxx.yyy.zzz === reg2.xxx.yyy.zzz);
    });
    it('可以复制日期', () => {
      const date1 = new Date();
      date1.xxx = { yyy: { zzz: 1 } };
      const date2 = deepClone(date1);
      assert(date1.source === date2.source);
      assert(date1.flags === date2.flags);
      assert(date1 !== date2);
      assert(date1.getTime() === date2.getTime());
      assert(date1.xxx !== date2.xxx);
      assert(date1.xxx.yyy !== date2.xxx.yyy);
      assert(date1.xxx.yyy.zzz === date2.xxx.yyy.zzz);
    });

代码实现

function deepClone(source) {
  if (source instanceof Object) {
    let dist;
    if (source instanceof Array) {
      dist = new Array();
    } else if (source instanceof Function) {
      dist = function () {
        return source.apply(this, arguments);
      };
    } else if (source instanceof RegExp) {
      dist = new RegExp(source.source, source.flags);
    } else if (source instanceof Date) {
      dist = new Date(source);
    } else {
      dist = new Object();
    }
    for (let key in source) {
      // for in会遍历原型上的属性
      if (source.hasOwnProperty(key)) {
        dist[key] = deepClone(source[key]);
      }
    }

    return dist;
  }

  return source;
}
image.png

目前我们粗略的实现了一个可以复制基本类型和常见对象的深拷贝

2.3 考虑环

const a = {}
a.self = a

测试用例:

it('环也能复制', () => {
      const obj1 = { name: 'sss' };
      obj1.self = obj1;
      const obj2 = deepClone(obj1);
      assert(obj1 !== obj2);
      assert(obj1.name === obj2.name);
      assert(obj1.self !== obj2.self);
    });

此时我们直接运行之前的代码,发现控制台直接报错了。因为对象每次深拷贝之后会生成一个全新的对象,所以此时会造成递归不能终止。因此我们需要先判断是否存在环,然后进行对象的拷贝。

我们创建一个cashe数组,里面存放origin和new

let cache = [];

function findCache(source) {
  for (let i = 0; i < cache.length; i++) {
    if (cache[i].origin && cache[i].origin === source) {
      return cache[i].new;
    }
  }
  return undefined;
}
function deepClone(source) {
  if (source instanceof Object) {
    let cachedDist = findCache(source);
    if (cachedDist) {
      return cachedDist;
    } else {
      let dist;
      if (source instanceof Array) {
        dist = new Array();
      } else if (source instanceof RegExp) {
        dist = new RegExp(source.source, source.flags);
      } else if (source instanceof Function) {
        dist = function () {
          return source.apply(this, arguments);
        };
      } else if (source instanceof Date) {
        dist = new Date(source);
      } else {
        dist = new Object();
      }
      cache.push({ origin: source, new: dist });
      for (let key in source) {
        // for in会遍历原型上的属性
        if (source.hasOwnProperty(key)) {
          dist[key] = deepClone(source[key]);
        }
      }

      return dist;
    }
  }
  return source;
}
image.png

你可能感兴趣的:(手写深拷贝)