Javascript 浅拷贝/深拷贝

拷贝功能是编程中的一个常用操作,主要分为深拷贝和浅拷贝,但也是经常容易犯错的一个知识点,今天来谈谈Javascript中的拷贝功能。

在JS中,常见的字符串拷贝操作是这样的:

str1 = '123';
str2 = str1;
console.log(str1);
str2 = '456';
console.log(str1);
console.log(str2);

非常简单,输出结果如下:

123
123
456


下面来看一段浅拷贝的例子:

obj1 = {a: 1};
obj2 = obj1;
console.log(obj2.a);
obj2.a = 2;
console.log(obj2.a);
console.log(obj1.a);

输出结果:

1
2
2

上面的代码可以看到,当我们把obj1赋值给obj2的后,改变了obj2中属性a的值,发现obj1中的a同样被改变了,因为当把obj1赋值给obj2后,obj1和obj2指向了同一个地址,这时候obj2和obj1是共享同一个内存空间的,可以参考下面的这种图:

Javascript 浅拷贝/深拷贝

所以改变obj2中的a,相当于改变了obj1中的a,这就是浅拷贝;

接着来看下深拷贝如何实现:

obj1 = {a: 1, b: {c: 2}};
obj2 = JSON.parse(JSON.stringify(obj1));
obj2.b.c = '3';
console.log(obj1.b.c);

输出结果:

2

可以看到通过把对象进行1次序列化+反序列化,把obj1赋值给obj2,然后修改obj2对象中的属性值,最后发现obj1中的属性值并没有改变,因为obj1和obj2独享自己的一份内存空间,这就实现了深拷贝操作,可以参照下面的这张图:

Javascript 浅拷贝/深拷贝

但是,上面的深拷贝方式其实是有问题的,不推荐这种写法,因为这种写法不能覆盖所有的深拷贝情况,来看几个反例:
反例1: 当要拷贝的对象中包含undefined的属性

obj1 = 
{
    a: 1, 
    b: 
    {
        c: 2,
        d: undefined
    }
}
obj2 = JSON.parse(JSON.stringify(obj1))
console.log(obj2)

输出结果:

{
    "a": 1,
    "b": {
        "c": 2
    }
}

从输出结果会发现拷贝后的obj2对象缺失了属性d

反例2: 当要拷贝的对象中包含function属性

obj1 = 
{
    a: 1, 
    b: 
    {
        c: 2,
        d: function name(params) {
            console.log(params);
        }
    }
}
obj2 = JSON.parse(JSON.stringify(obj1))
console.log(obj2)

输出结果:

{
    "a": 1,
    "b": {
        "c": 2
    }
}

从输出结果会发现拷贝后的obj2对象缺失了属性d。
反例3: 当要拷贝的对象中包含对象属性

obj1 = 
{
    a: 1, 
    b: 
    {
        c: 2,
        d: new RegExp('\w+')
    }
}
obj2 = JSON.parse(JSON.stringify(obj1))
console.log(obj2)

输出结果:

{
    "a": 1,
    "b": {
        "c": 2,
        "d": {}
    }
}

从输出结果会发现拷贝后的obj2对象缺失了属性d。

从上面的
3个反例中可以看到JSON.parse(JSON.stringify())这种方式并不适合用来做深拷贝。

下面来介绍在Javascript中深拷贝的正确姿势:

const _ = require('lodash');
const externalObject = {
  animal: 'Gator'
};
const originalObject = {
  a: 1,
  b: 'string',
  c: false,
  d: externalObject
};
const deepClonedObject = _.clonedeep(originalObject);
externalObject.animal = 'Lizard';
console.log(originalObject);
console.log(deepClonedObject);

输出结果:

{ a: 1, b: 'string', c: false, d: { animal: 'Lizard' } }
{ a: 1, b: 'string', c: false, d: { animal: 'Crocodile' } }

从输出结果可以看到,用lodash这个三方库中的clonedeep方法做到了对象的深度拷贝,拷贝后的对象和原对象做到了完全的隔离,如果要完成深拷贝的功能,推荐使用这种方式。

以上主要介绍了Javascript中深拷贝和浅拷贝的常见用法和误区。

发表评论
留言与评论(共有 0 条评论) “”
   
验证码:

相关文章

推荐文章