JavaScript之深浅拷贝

        上篇博客中简单介绍了JS中的引用类型,本篇博客就简单介绍下JS的深浅拷贝,以后有时间再对深浅拷贝做个扩展。原始类型是保存在栈内存中的,对于它们的复制可以理解为“真实”复制,即重新开辟栈内存并将原来的值copy一份放进去。那么修改copy的那个值并不会影响原始值,因为它们是独立的。

JavaScript之深浅拷贝_第1张图片

         先看看原始类型和引用类型的存储方式:(图片摘自https://www.cnblogs.com/onlycare/p/9791826.html)

JavaScript之深浅拷贝_第2张图片

        复杂的数据类型即引用类型,它的值是对象,保存在堆内存中,包含引用类型值的变量实际上包含的并不是对象本身,而是一个指向该对象的指针。从一个变量向另一个变量复制引用类型的值,复制的其实是指针,因此两个变量最终都指向同一个对象。

const a = []
b = a
console.log("a:",a, "; b:",b)         //输出: a: [ ] ; b: [ ]
b[0] = 1
console.log("a:",a, "b:",b)           //输出: a: [1] ; b: [1]

上面代码,只是将a的地址给了b,因而对b进行操作,会影响到a的值,这在很多情况下并不是我们想要的。

【浅拷贝】

首先可以通过Object.assign来解决这个问题,很多人认为这个函数是用来深拷贝的。其实并不是,Object.assign只会拷贝所有属性值到新的对象中,如果属性值是对象的话,拷贝的是地址,所以并不是深拷贝。

let a = {
    age: 1
}

let b = Object.assign({}, a)
a.age = 2
console.log(b.age)   // 1

除了Object.assign之外,我们还可以通过展开运算符 ... 来实现浅拷贝,

let a = {
    age: 1
}
let b = {...a}
a.age = 2
console.log(b.age) //1

通常来说浅拷贝就能解决大部分问题了,但是当我们遇到如下情况,就会发现浅拷贝无法满足我们的需要了。

let a = {
    age:1,
    jobs: {
        first: 'IT'
    }
}
let b = {...a}
a.jobs.first = 'PM'
console.log(b.jobs.first)  // PM

对于第一层拷贝问题,浅拷贝可以解决,但如果接下去的值中还有对象的话,那么两者享有相同的地址。要解决这个问题,就不得不使用深拷贝了。 

 

【深拷贝】

使用JSON.parse(JSON.stringify(a))

let a = {
    age: 1,
    jobs: {
        first: 'IT'
    }
}
let b = JSON.parse(JSON.stringify(a))
a.jobs.first = 'PM'
console.log(b.jobs.first) // IT

难道深拷贝就这么简单?并不是,该方法虽然可以实现深拷贝,但是存在诸多局限:

1、会忽略undifined;2、会忽略symbol;3、不能序列化函数;4、不能解决循环引用的对象。

JSON.parse(JSON.stringify())可以解决大部分深拷贝的问题,更高级的可以用lodash的深拷贝函数

function deepClone(obj){
    function isObject(o){
        return(typeof o === 'object' || typeof o === 'function') && o !== null
    }
    
    if(!isObject(obj)){
        throw new Error('非对象')
    }
    
    let isArray = Array.isArray(obj)
    let newObj  = isArray ? [...obj] : {...obj}
    Reflect.ownKeys(newObj).forEach(key => {
        newObj[key] = isObject(obj[key]) ? deepClone(obj[key]) : obj[key]
    })

    return newObj
}

let obj = {
    a: [1, 2, 3],
    b: {
        c: 2,
        d: 3
    }
}
let newObj = deepClone(obj)
newObj.b.d = 2
console.log(obj.b.d)  // 3

 

 

你可能感兴趣的:(前端)