关于面试的深拷贝

最近看到面试题,是关于深拷贝的

然后就即兴思考了很多,写了本偏文章。

初版深拷贝,也就是视频里将的那版

function deepClone(source) {
        // 输入是数组或是对象
        let target = source.constructor === Array ? [] : {};
        for (let key in source) {
          // 遍历,如果是对象或是数组,递归拷贝
          if (
            source[key].constructor === Array ||
            source[key].constructor === Object
          ) {
            target[key] = deepClone(source[key]);
          } else {
            //否则是简单数据类型,直接赋值
            target[key] = source[key];
          }
        }
        return target;
      }

这里有一个疑问就是数组的constructor可以被改变,也就是:

let a = [1, 2];
console.log(a.constructor);
a.constructor = Object;
console.log(a.constructor);

虽然if判断处处理逻辑是一样的,但是target处的判断有问题,

那么尝试修改判断方式,使用instanceof

console.log(a instanceof Array);
console.log(a instanceof Object);

但是显然,不管怎么判断都是true,方式不可行,还有办法吗

有的,使用Array.isArray

console.log(Array.isArray(a));
let b = { id: 0 };
console.log(Array.isArray(b));

可以看出,就算是constructor更改了,依然可以判断出Array

而且对象返回的是false,因此尝试重写深拷贝:

function deepClone(source) {
        // 这里的输入只会数组或者是对象
        let target = Array.isArray(source) ? [] : {};
        for (let key in source) {
          // 有线考虑数组
          if (Array.isArray(source[key])) {
            target[key] = deepClone(source[key]);
          } else if (source[key].constructor === Object) {
            // 接着考虑对象
            target[key] = deepClone(source[key]);
            // 再写其他数据类型的逻辑
          } else {
            target[key] = source[key];
          }
        }
        return target;
      }

可是代码太冗长了,而且逻辑是一样的,换一个思路

let obj = {
        a: [1, 2],
        b: { c: 1, d: "2", e: [1, 2], f: { g: 1, h: "abc" } },
        i: "abc",
        j: 1,
      };
      for (let key in obj) {
        console.log(obj[key] instanceof Object);
      }

可以区分数组和对象都返回true,而且处理逻辑相同,

那么方法还可以优化如下

function dc(source) {
        let target = Array.isArray(source) ? [] : {};
        for (key in source) {
          if (source[key] instanceof Object) {
            target[key] = deepClone(source[key]);
          } else {
            target[key] = source[key];
          }
        }
        return target;
      }
 let newobj = dc(obj);
      console.log(obj);
      newobj.x = "y";
      console.log(newobj);

可以看到修改拷贝后的对象源对象没有受到改变

已知生活中可以使用以下方法实现深拷贝解决80%的问题:

let dcjson = (source) => {
        return JSON.parse(JSON.stringify(source));
      };
      let newobj1 = dcjson(obj);
      newobj1.x = "y";
      console.log(obj);
      console.log(newobj1);

剩下20%因为函数等无法拷贝而不能使用JSON

obj.y = function () {
        console.log("aaa");
      };
      console.log(obj);
      console.log(dc(obj));
      console.log(dcjson(obj));

结果中,自编dc方法拷贝的函数已经不是函数,json方法未拷贝

那有办法拷贝函数吗,当然有,先看一个浅拷贝的方式

let newobj2 = { ...newobj };
      console.log(newobj2);
      // 函数是可以通过浅拷贝的方式被拷贝的
      let newobj3 = { ...obj };
      console.log(newobj3);

但是只适合部分场景

周所众知,有一个库叫lodash。所以想要拷贝包括函数在内的更多数据类型,还是用lodash库吧。

var objects = [{ 'a': 1 }, { 'b': 2 }];
 
var deep = _.cloneDeep(objects);
console.log(deep[0] === objects[0]);
// => false

汇总:

      //初版深拷贝
      function deepClone(source) {
        // 输入是数组或是对象
        let target = source.constructor === Array ? [] : {};
        for (let key in source) {
          // 遍历,如果是对象或是数组,递归拷贝
          if (
            source[key].constructor === Array ||
            source[key].constructor === Object
          ) {
            target[key] = deepClone(source[key]);
          } else {
            //否则是简单数据类型,直接赋值
            target[key] = source[key];
          }
        }
        return target;
      }
      //这里有一个疑问就是数组的constructor可以被改变,也就是:
      let a = [1, 2];
      console.log(a.constructor);
      a.constructor = Object;
      console.log(a.constructor);
      // 虽然if判断处处理逻辑是一样的,但是target处的判断有问题,
      // 那么尝试修改判断方式,使用instanceof
      console.log(a instanceof Array);
      console.log(a instanceof Object);
      // 但是显然,不管怎么判断都是true,方式不可行,还有办法吗
      // 有的,使用Array.isArray
      console.log(Array.isArray(a));
      let b = { id: 0 };
      console.log(Array.isArray(b));
      // 可以看出,就算是constructor更改了,依然可以判断出Array
      // 而且对象返回的是false,因此尝试重写深拷贝:
      function deepClone(source) {
        // 这里的输入只会数组或者是对象
        let target = Array.isArray(source) ? [] : {};
        for (let key in source) {
          // 有线考虑数组
          if (Array.isArray(source[key])) {
            target[key] = deepClone(source[key]);
          } else if (source[key].constructor === Object) {
            // 接着考虑对象
            target[key] = deepClone(source[key]);
            // 再写其他数据类型的逻辑
          } else {
            target[key] = source[key];
          }
        }
        return target;
      }
      // 可是代码太冗长了,而且逻辑是一样的,换一个思路
      let obj = {
        a: [1, 2],
        b: { c: 1, d: "2", e: [1, 2], f: { g: 1, h: "abc" } },
        i: "abc",
        j: 1,
      };
      for (let key in obj) {
        console.log(obj[key] instanceof Object);
      }

      // 可以区分数组和对象都返回true,而且处理逻辑相同,
      // 那么方法还可以优化如下

      function dc(source) {
        let target = Array.isArray(source) ? [] : {};
        for (key in source) {
          if (source[key] instanceof Object) {
            target[key] = deepClone(source[key]);
          } else {
            target[key] = source[key];
          }
        }
        return target;
      }

      let newobj = dc(obj);
      console.log(obj);
      newobj.x = "y";
      console.log(newobj);
      // 已知生活中可以使用以下方法实现深拷贝解决80%的问题
      let dcjson = (source) => {
        return JSON.parse(JSON.stringify(source));
      };
      let newobj1 = dcjson(obj);
      newobj1.x = "y";
      console.log(obj);
      console.log(newobj1);
      // 剩下20%因为函数等无法拷贝而不能使用JSON
      obj.y = function () {
        console.log("aaa");
      };
      console.log(obj);
      console.log(dc(obj));
      console.log(dcjson(obj));
      // 结果中,自编dc方法拷贝的函数已经不是函数,json方法未拷贝
      // 那有办法拷贝函数吗,当然有,先看一个浅拷贝的方式
      let newobj2 = { ...newobj };
      console.log(newobj2);
      // 函数是可以通过浅拷贝的方式被拷贝的
      let newobj3 = { ...obj };
      console.log(newobj3);
      // 但是只适合部分场景
      // 周所众知,有一个库叫lodash,以下是lodash深拷贝源码
    var objects = [{ 'a': 1 }, { 'b': 2 }];
 
var deep = _.cloneDeep(objects);
console.log(deep[0] === objects[0]);
// => false

  • 10
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值