JS 深拷贝详解

1.这里实现了深拷贝是因为在基本数据类型String Number 都可以实现深拷贝因为它只有一层 ,且每次改变的变量的值都是改变整个变量这样在堆中又开辟了一个内存空间,没有嵌套的情况下直接修改整个变量的方法在对象和方法中也适用
var a = 1
var b = a
a 打印结果为 1
b 打印结果为 1
a = 2
a 打印结果为 2
b 打印结果为 1

2.这里实现了深拷贝的原因与第1条是一样的,因为改成a变量整个值,所以在堆中又开辟了一个新空间
var a = [1,2,3]
var b = a
a 打印结果为 (3) [1, 2, 3]
b 打印结果为 (3) [1, 2, 3]
a = [3,2,1]
a 打印结果为 (3) [3, 2, 1]
b 打印结果为 (3) [1, 2, 3]

3.这里没有实现深拷贝是因为 这里的a是引用数据类型,且修改a的值时改变的是下标0的数据,并没有整个a变量全部修改,所以并没有开辟新的内存空间,导致a和b都指向了同一个堆,故而打印的结果是一样的
var a = [1,2,3]
var b = a
a 打印结果为 (3) [1, 2, 3]
b 打印结果为 (3) [1, 2, 3]
a[0] = 2
a 打印结果为 (3) [2, 2, 3]
b 打印结果为 (3) [2, 2, 3]

4.这里实现了深拷贝是因为给b赋值的时候使用了扩展运算符,这样就等于为b在堆里面新开了一个内存空间,故而实现了深拷贝
var a = [1,2,3]
var b = [...a]
a 打印结果为 (3) [1, 2, 3]
b 打印结果为 (3) [1, 2, 3]
a[0] = 2
a 打印结果为 (3) [2, 2, 3]
b 打印结果为 (3) [1, 2, 3]

5.这里使用了扩展运算符但是却没有像第4条那样实现深拷贝,因为这里的a是二维数组,a和b下标为3的数据来源于同一个堆,故而打印结果为一样
var a = [1,2,3,[4,5]]
var b = [...a]
a 打印结果为 [0:1,1:2,2:3,3:[0:4,1:5]]
b 打印结果为 [0:1,1:2,2:3,3:[0:4,1:5]]
a3 = 6
a 打印结果为 [0:1,1:2,2:3,3:[0:6,1:5]]
b 打印结果为 [0:1,1:2,2:3,3:[0:6,1:5]]

根据以上结论我们得出结论:

        1. 无论是不是使用了扩展运算符只要是直接修改了a变量整体数据那就不影响b,因为变量整体修改的话相当于在堆里面新开辟了一个内存
        2. 扩展运算符支持深拷贝只建立在没有嵌套数据的情况下。

下面我们来说遇到多层嵌套的情况下怎么进行深拷贝

  1. 使用JSON.stringify转换为JSON字符串 后使用JSON.parse解析为JS的对象或值
    var a = [1,2,3,[4,5]]
    var b = JSON.parse(JSON.stringify(a))
    a 打印结果为 [0:1,1:2,2:3,[0:4,1:5]]
    b 打印结果为 [0:1,1:2,2:3,[0:4,1:5]]
    a3 = 11
    a 打印结果为 [0:1,1:2,2:3,[0:4,1:11]]
    b 打印结果为 [0:1,1:2,2:3,[0:4,1:5]]

2.但是需要注意的是,JSON.stringify无法深拷贝函数。如下代码
var a = {function(){console.log('hello')}}
a 打印结果为 {function: ƒ}
var b = JSON.parse(JSON.stringify(a))
b 打印结果为 {}

3.这个时候我们就需要用到递归遍历对象并手动进行拷贝 或者 Lodash的_.cloneDeep方法 。函数序列化并还原是相当复杂的操作,并且有安全风险,因为还原的函数将在不受控制的上下文中执行。应谨慎使用和处理包含函数的序列化数据
const _ = require('lodash');

const obj = { func: function() { console.log('Hello!'); } };
const clonedObj = _.cloneDeep(obj);
console.log(clonedObj);
// 输出: { func: [Function: func] }

你可能感兴趣的:(JS 深拷贝详解)