欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

JavaScript的深浅复制

程序员文章站 2024-01-23 11:25:46
JavaScript的深浅复制 为什么有深复制、浅复制? JavaScript中有两种数据类型,基本数据类型如 、`null boolean number string Object`。简单数据类型只存储在内存中的 栈区 ,复制的时候是值传递给新的索引。而复杂数据类型由栈区和 堆区 共同储存,栈区执 ......

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. 深拷贝的终极探索(99%的人都不知道) - 颜海镜 - segmentfault 思否

上一篇: golang grpc 负载均衡的方法

下一篇: