JavaScript的深浅复制

JavaScript的深浅复制

为什么有深复制、浅复制?

JavaScript中有两种数据类型,基本数据类型如undefinednullbooleannumberstring,另一类是Object。简单数据类型只存储在内存中的栈区,复制的时候是值传递给新的索引。而复杂数据类型由栈区和堆区共同储存,栈区执行同样的操作,只是把堆地址复制了一份,而真实数据在堆区中依然只有一份。
为了不影响原有数据,那么我们就新建一个对象,遍历原有对象的属性赋值到新属性。

let newObj = {}
for (let prop in obj) {
  newObj[prop] = obj[prop]
}

上面这个循环也可以用Object.assign({}, obj);来实现。
这样做是否解决问题?未必,因为Object中可以嵌套Object,如果原有对象属性中有复杂数据类型,那么新的对象中也只能得到一个地址。这种情况被称为浅复制。我们希望能将对象中的对象,无论多少层,都能复制一份,能达到这种效果的,称为深复制

深复制的几种方法

首先假设有数据

let obj = {
    a: 23,
    b: [0, 1, [2, 3], function() {console.log('in array')}, undefined],
    c: {k: 'value'},
    d: function() {console.log('a')}
    }

JSON.parse(JSON.stringify(obj))

let newObj = JSON.parse(JSON.stringify(obj))
newObj.newKey = 'newValue'
console.log(obj)
console.log(newObj)

如果处理对象只是简单的键值对,这个方法效果不错。

这个方法的缺点

  • 无法复制函数
  • 忽略undefined
  • 无法处理Set、Map、Symbol类型(即使用上repalce参数)
  • 原有的原型链会消失
  • 循环引用的对象会报错

    递归法

    因为要处理属性的值也是Object这种情况,自然可以想到递归这种处理方法。
function deepCopy(oldObj, newObj) {
    let obj = newObj || {} 
    for (let i in oldObj) {
        if (oldObj[i] === obj) { // 防止循环引用
            continue
        }

        if (typeof oldObj[i] === 'object') {
            // obj[i] = (oldObj[i].constructor === Array) ? [] : {}
            obj[i] = oldObj[i] instanceof Array ? [] : {}
            deepCopy(oldObj[i], obj[i])
        } else {
            obj[i] = oldObj[i]
        }
    }
    return obj;
}

这样就能处理一个嵌套了Object和Array等复杂变量的对象。但是对象中还可能包含Date和RegExp对象、Set、Map……

Lodash库

const lodash = require('lodash')
let newObj = lodash.cloneDeep(obj)

数组的复制

let arr = [1, 2, 3, [4, 5], {a: 1}]

let copy = arr
copy.push(6)

let copy1 = [...arr]
copy1.push(999)

let copy2 = Array.from(arr)
copy2.push(888)

let copy3 = arr.slice()
copy3.push(777)

// 以上方法都是浅拷贝
arr[4].a = 2

console.log(arr)   // [ 1, 2, 3, [ 4, 5 ], { a: 2 }, 6 ]
console.log(copy1) // [ 1, 2, 3, [ 4, 5 ], { a: 2 }, 6 ]
console.log(copy2) // [ 1, 2, 3, [ 4, 5 ], { a: 2 }, 6, 888 ]
console.log(copy3) // [ 1, 2, 3, [ 4, 5 ], { a: 2 }, 6, 777 ]

参考连接

  1. 摸索 JS 内深拷贝的最佳实践 - 简单易懂的前端角 - SegmentFault 思否
  2. 理解JavaScript:不可变的原始值与可变的对象引用
  3. [ JS 进阶 ] 基本类型 引用类型 简单赋值 对象引用 - kraaas前端博客 - SegmentFault 思否
  4. 什么是js深拷贝和浅拷贝及其实现方式
  5. js 深拷贝 vs 浅拷贝 - 掘金
  6. 深拷贝的终极探索(99%的人都不知道) - 颜海镜 - SegmentFault 思否

猜你喜欢

转载自www.cnblogs.com/zoiyin/p/js_copy.html
今日推荐