写在前面

深拷贝应该是前端面试中经常被问到的问题之一,搞定它可以让我们在面试中如鱼得水。那么什么是深拷贝呢?它和浅拷贝有什么区别呢?如何实现一个深拷贝?相信看完这篇文章你就能回答上面的问题了。

一、简单解释

我们都知道js的数据类型包括两种:基本数据类型和引用数据类型。我们今天所说的深拷贝和浅拷贝都只针对引用数据类型,浅拷贝只复制指向某个对象的指针,而不复制对象本身,新旧对象还是共享同一块内存;但深拷贝会创造一个一模一样的对象,新对象跟原对象不共享内存,修改新对象不会改到原对象。纸上得来终觉浅,先来一个例子,借助例子大家会看的更明白。

var obj = {
    name:'zhang',
    say:function(){
        console.log('hi');
    },
    a:undefined,
    b:{c:'hello'}
}

二、浅拷贝

浅拷贝常用方法:

//方法一:
var newObj = {...obj}
//方法二:
var newObj = Object.assign({},obj)

接下来我们看看浅拷贝有什么特性:

var obj2 = {...obj};
    obj.b.c = 'world'
    console.log(obj.b.c);  //world
    console.log(obj2.b.c); //world
    console.log(obj === obj2); //true

从代码的运行结果我们可以得知,拷贝后的新对象和旧对象还是共享同一块内存空间。当我们修改新对象的某个属性时,旧对象的那个属性也被修改了,显然这不是我们想要的结果。

三、深拷贝

上面浅拷贝的结果并不是我们想要的,那么深拷贝会是什么结果呢?深拷贝有多种方法,在这里我们只讲两种:

3.1利用JSON.stringfy()和JSON.parse()实现深拷贝
var obj2 = JSON.parse(JSON.stringify(obj))
    obj2.b.c = 'world'
    console.log(obj.b.c);  //hello
    console.log(obj2.b.c); //world
    console.log(obj === obj2); //false

同样的代码,和上面的浅拷贝运行结果完全不一样。看到这里是不是觉得深拷贝很简单啊,一行代码就实现了。事实并没有那么简单,我们再打印一下拷贝的obj2对象

复制容器里的文件到本地 拷贝内容_面试


是的,你没有看错,深拷贝后的obj2对象竟然没有了属性a和say方法,这有点坑啊!注意: 使用这种方法拷贝对象时,当对象的属性值是undefined、function、symbol类型时,在转换过程中会被忽略。而且这种方法也无法实现对RegExp等特殊对象的克隆。所以这种方法还是要谨慎使用。

3.2构造深拷贝函数
function clone(obj){
        var cloneObj = Array.isArray(obj) ? [] : {};
        if(obj && typeof obj === 'object'){
            for(key in obj){
                if(obj.hasOwnProperty(key)){
                    //如果对象的属性还是对象  则递归调用
                    if(obj[key] && typeof obj[key] === 'object'){
                        cloneObj[key] = clone(obj[key])
                    }else{
                        cloneObj[key] = obj[key]
                    }
                }
            }
        }
        return cloneObj;
    }

代码应该很好理解,关键的一点就是判断对象的属性,如果还是对象就递归赋值。同样我们测试一下:

var obj2 = clone(obj);
    obj2.b.c = 'world'
    console.log(obj.b.c);  
    console.log(obj2.b.c); 
    console.log(obj === obj2); 
    console.log(obj);
    console.log(obj2);

运行结果:

复制容器里的文件到本地 拷贝内容_浅拷贝_02


ok,结果没毛病!

后话

看到这里大家都能够回答我们在文章开头提出的那三个问题了吧。其实深拷贝还有其他方法,比如jquery的extend方法、lodash函数库的cloneDeep方法等,不过这些方法很少用到,有兴趣的同学可以了解一下。