js对象数组深浅拷贝

755 阅读3分钟

什么是深拷贝,什么是浅拷贝? 可能这样的问题时刻在开发环节困扰你

最简单理解是:

修改一个值,对复制的对象有没有影响?有,就是浅拷贝; 没有,就是深拷贝

简单数据类型

  • Boolean
  • String
  • Symbol
  • Number
  • null 和 undefined

在以上几种类型中,将A对象的值复制到B对象,然后修改B对象的值,对A没有影响

let name = "张三"
// 拷贝
let cName = name

cName = "李四"

console.log(name, cName) // 张三 李四

Symbol

let name = Symbol("妞妞")
// 拷贝
let cName = name

cName = "李四"

console.log(name, cName) // Symbol(妞妞) "李四"

以上就列举2种,其他的结果是一样的,拷贝过后的B数据的修改,是不会影响原始数据A对象的值

引用类型

常见的引用类型如下:

  • Object
  • Array

项目中一般不涉及下面引用类型的拷贝,不作展开

  • RegExp
  • Date
  • Function
  • Set
  • Map

数组深浅拷贝

浅拷贝

新数组和拷贝的数据,指向的都是同一个引用地址,修改其中一个,会影响另一个的值

let originArr = [1,2,3]
let cArr = originArr

// 修改cArr
cArr[1] = "我是修改的内容"
// 都变成了 [1, "我是修改的内容", 3]
console.log(originArr, cArr) 

深拷贝一层

concat

concat 这种,只能拷贝一维数组,数组中有对象,多层数组,则仍旧存在引用,源数据与拷贝对象存在相互影响的问题

let orginArr = [1, {name: "账上", ho: ["jack"]}, 3]

let n = [].concat(orginArr)

n[1].name = "jojo"
console.log(n[1] === orginArr[1]) // true -- 说明影响了

// 简单结构
let t1 = [1,2,3]
let t2 = [].concat(t1)
t2[1]='milk'
console.log(t1, t2)

slice

从下图中可见,slice也只能copy一层,内部有引用类型的结构,是会存在相互影响的

let originArr = [1, {name: "jojo"}, 222]

let c = originArr.slice(0)

c[1].name = "mack"
console.log(originArr, c)

let t1 = [1,2,3]
let t2 = t1.slice(0)

t2[1] = 'good'

console.log(t1,t2)

扩展运算符

扩展运算符,也只能拷贝外面的一层,如果内部是对象,或者数组等引用类型,则源数据和拷贝后的对象,依旧会相互影响

let t1 = [1,2,3]

let [...t2] = t1

t2[0] = 'jojo'
console.log(t1)
console.log(t2)

深拷贝多层

遍历

let orginArr = [1, {name: "账上", ho: ["jack"]}, 3]

function copyTo(origin) {
    let target = Array.isArray(origin) ? [] : {}

    for (let i in origin) {
        if (typeof origin[i] === "object") {
            // 递归
            target[i] = copyTo(origin[i])
        } else {
            target[i] = origin[i]
        }

    }

    return target
}

let n = copyTo(orginArr)

n[0] = "hi"

console.log(n)
console.log(orginArr)

这种方式简单,理解无难度

JSON.strinify

这种属于简便方法,且只能实现纯数据的克隆,如果数组中某个元素是function 则直接忽略,这种方式的拷贝,就是将数据变成基本类型,再变成一个对象,会重新分配一块新的存储空间

function deepClone(obj) {
    return JSON.parse(JSON.stringify(obj))
}

let a = {
    a: 1,
    b: function() {
        
    }
}

let cA = deepClone(a)

console.log(cA) // {a: 1}

let orginArr = [1, 2, 3, {name: "jojo"}]

let t2 = deepClone(orginArr)

t2[0] = "fresh"
t2[3].name = "milk"

console.log('拷贝后的数据', t2)
console.log("源数据", orginArr)

从上面的图中可以看到,是深度拷贝了对象,且忽略了function

数组其他方法遍历,达到浅拷贝效果

filter

这种,只能复制一层,内部有多层,则会相互影响

let list = [
   1,2,3,4
]

let n = list.filter(p => p)

n[1] = 'jojo'
console.log(list)
console.log(n)

map

这个也只能复制一层

let list = [
   1,2,3,4
]

let n = list.map(p => p)

n[1] = 'jojo'
console.log(list)
console.log(n)


对象

对象的浅拷贝

扩展运算符

只能拷贝一层

let t1 = {
    a: 1,
    b: [1,2,3]
}

let {...t2} = t1
t2.a = "good"
t2.b.push(5555)
console.log(t1)
console.log(t2)

对象的深拷贝

递归遍历

就是上面的方法 copyTo

JSON.strinify

这个适用数组,对象,深度拷贝