首页 文章详情

面试官问:深拷贝 浅拷贝 怎么实现一个深拷贝 出现递归循环怎么办,结束条件是什么?

前端人 | 520 2021-03-31 11:32 0 0 0
UniSMS (合一短信)


关注公众号 前端人,回复“加群

添加无广告优质学习群

今天的题目介绍:

【虾皮一卷#03题】,每周题目周六统一录入文末小程序中!

深拷贝和浅拷贝的区别

深拷贝在于引用类型的时候,浅拷贝只复制地址值,实际上还是指向同一堆内存中的数据,深拷贝则是重新创建了一个相同的数据,二者指向的堆内存的地址值是不同的。这个时候修改赋值前的变量数据不会影响赋值后的变量。

深拷贝实现

1.通过递归方式实现深拷贝
function deepClone(obj{
    var target = {};
    for(var key in obj) {
        if (Object.prototype.hasOwnProperty.call(obj, key)) {
            if (typeof obj[key] === 'object') {
                target[key] = deepClone(obj[key]); 
            } else {
                target[key] = obj[key];
            }
        }
    }
    return target;
}
2.通过json的方式实现
function (obj{
    let tmp = JSON.stringify(obj); 
    let result = JSON.parse(tmp); 
    return result;
}
3.通过Object.create()实现
function deepCopy(obj{
  var copy = Object.create(Object.getPrototypeOf(obj));
  var propNames = Object.getOwnPropertyNames(obj);
  
  propNames.forEach(function(name{
    var desc = Object.getOwnPropertyDescriptor(obj, name);
    Object.defineProperty(copy, name, desc);
  });
  
  return copy;
}

var obj1 = { a1b: {bc50dc100be: {bea1}} };
var obj2 = deepCopy(obj1);
console.log(obj2)
obj1.a = 20;
console.log(obj1)
console.log(obj2)
//Object {a: 1, b: Object}
//Object {a: 20, b: Object}
//Object {a: 1, b: Object}

浅拷贝

如果我们要复制对象的所有属性都不是引用类型时,就可以使用浅拷贝,实现方式就是遍历并复制,最后返回新的对象。

function shallowCopy(obj{
    var copy = {};
    // 只复制可遍历的属性
    for (key in obj) {
        // 只复制本身拥有的属性
        if (obj.hasOwnProperty(key)) {
            copy[key] = obj[key];
        }
    }
    return copy;
}

如上面所说,我们使用浅拷贝会复制所有引用对象的指针,而不是具体的值,所以使用时一定要明确自己的需求,同时,浅拷贝的实现也是最简单的。

JS内部实现了浅拷贝,如Object.assign(),其中第一个参数是我们最终复制的目标对象,后面的所有参数是我们的即将复制的源对象,支持对象或数组,一般调用的方式为

var newObj = Object.assign({}, originObj);

这样我们就得到了一个新的浅拷贝对象。另外[].slice()方法可以视为数组对象的浅拷贝。

深拷贝出现递归循环怎么办?结束条件是什么?

问题描述

js对象clone是RIA编程中常用方法,但是对象属性之间的循环引用会导致clone的递归进入死循环。

js 代码

var  a = {pa1:'av1',pa2:'av2'};  
var  b = {pb1:'bv1',pb2:'bv2'};  
a.pa3 = b;  
//b.pb3 = a;   
var  c = cloneobj(a);  

如果不包含注释掉的一行,clone是可以正常进行的。

但是如果引入这一行,即出现了js对象属性的循环引用,clone将进入递归的死循环。

现象

浏览器能够很好的处理这种错误,并抛出“too much recursion"错误,并定位到相应的代码行。(小声说一句:如果写个js死循环就把浏览器搞死了,B/S应用就没法混了)

办法

如何解决了,通常的办法就是限制递归的深度,例如DWR的‘DWRUtil.toDescriptiveString’

但是窃以为这个并非好的解决办法,因为了对应用造成极大的限制。

我的办法就是,在clone的过程中,记住每个已经clone的对象属性, 并且在对对象进行深度clone之前,首先检查是否已经clone过了,如果是,则返回已clone的引用即可。

因此只要放开示例代码的注释行" // return os[m];"就OK了。

参考资料

  • https://my.oschina.net/u/1440018/blog/543245
  • https://segmentfault.com/a/1190000011403163
  • https://segmentfault.com/a/1190000018193387

最后

关注公众号,置顶公众号,鬼哥每天解答一道大厂面试题,一起前端进阶

  1. 公众号里回复关键词资料包领取我整理的进阶资料包
  2. 公众号里回复关键词加群,加入前端进阶群
  3. 文章点个在看,支持一下把!

点击关注我们↓

题库小程序


good-icon 0
favorite-icon 0
收藏
回复数量: 0
    暂无评论~~
    Ctrl+Enter