js中深拷贝和浅拷贝

212 阅读2分钟

说起js引用类型,绕不开的点就是浅拷贝和深拷贝。

一、赋值(=)与浅拷贝

什么是赋值?什么又是浅拷贝?(只针对引用类型)

赋值就是将引用类型的引用地址赋予到变量上保存的过程

浅拷贝是对所引用的对象做一次一级拷贝赋值到新的变量上,下面用代码来进行演示

var obj = {
    name: 'eric',
    age: 26,
    language: ['chinese', 'english']
}
var obj1 = obj // 赋值
var obj2 = simpleClone(obj)
function simpleClone (obj) {
    var ret = {}, key
    for (key in obj) {
        if(obj.hasOwnProperty(key)) {
            ret[key] = obj[key]
        }
    }
    return ret
}

// 得到的三个变量的值为:
// obj = {
//    name: 'eric',
//    age: 26,
//    language: ['chinese', 'english']
// }
// obj = {
//    name: 'eric',
//    age: 26,
//    language: ['chinese', 'english']
// }
// obj = {
//    name: 'eric',
//    age: 26,
//    language: ['chinese', 'english']
// }obj.name = 'super6'
obj.sex = 'man'
console.log(obj1.name) // super6
console.log(obj1.sex) // man
console.log(obj2.sex) // undefined

obj.language.push('japanese')
console.log(obj.language) // ['chinese', 'english', 'japanese']
console.log(obj1.language) // ['chinese', 'english', 'japanese']
console.log(obj2.language) // ['chinese', 'english', 'japanese']

总结就是,浅拷贝就是将一个对象a中第一级的键值对拷贝到新的变量b中,传值的过程,基本类型传值,引用类型传址。a与b在堆内存是不同的存储地址。

二、深拷贝

深拷贝就是将一个对象a完全的拷贝到新变量b中,这其中包含了对象a中的子对象

JS原生不支持深拷贝,Object.assign和{...obj}都属于浅拷贝。

JSON.stringfy和JSON.parse

js中实现深拷贝的最简单的方法了,原理也简单:就是将对象转换为字符串,然后再通过JSON.parse新建一个对象,但是这种方法也有他的局限性:

1、不能复制function、正则和Symbol

2、循环引用报错

3、相同的引用会被重复复制

let obj = {         
    reg : /^abc$/,
    fun: function(){},
    syb:Symbol('stttr'),
    asd:'abc'
}; 
let cp = JSON.parse(JSON.stringify(obj));
console.log(cp);

打印的结果:

正则、Symbol和function都没有被正确的复制

var obj = {
    name: 'eric',
    age: 26
}
var lang = ['chinese', 'english']
obj.lang = lang
obj.language = lang
var cp = JSON.parse(JSON.stringify(obj))
obj.lang.push('japanese')
cp.lang.push('japanese')
console.log(obj.language)
console.log(cp.language)

obj.language // ['chinese', 'english', 'japanese']
cp.language // ['chinese', 'english']

我们期望的结果是cp中的lang和language依然是指向的同一个堆数据,但是JSON实现深克隆的过程中lang和language分别指向了不同的堆数据(对象)

递归实现深拷贝

对于这种嵌套对象的拷贝,我们很容易就会想到递归。

思路也是很简单的:对于简单类型,直接复制。对于引用类型,递归复制它的每一个属性。

同时我们需要解决的问题:

1、循环引用

2、相同引用

3、不同的类型

function deepClone (o) {
    let copyList = [] 
    function _deepC (o) {
        if (typeof o !== 'object' || !o) return o
        for (let i = 0, len = copyList.length; i < len; i++) {
            if (copyList[i].taget === o) {
                return copyList[i].copyTarget
            }
        }
        let obj = {}
        if (Array.isArray(o)) {
            obj = []
        }
        copyList.push({
            target: o,
            copyTarge: obj
        })
        Object.keys(o).forEach(key => {
            if (obj[key]) return
            obj[key] = _deepC(o[key])
        })
        return obj
   }
    return _deepC(o) 
}