美文网首页摄影
浅复制(浅拷贝)和深复制(深拷贝)

浅复制(浅拷贝)和深复制(深拷贝)

作者: locky丶 | 来源:发表于2019-04-29 19:29 被阅读0次

    js存储机制

    JS中对象分为基本类型和复合(引用)类型,基本类型存放在栈内存,复合(引用)类型存放在堆内存。
    堆内存用于存放由new创建的对象,栈内存存放一些基本类型的变量和对象的引用变量。

    1.基本类型
    如 变量,在创建时会直接复制一份。

    var a = 'hello men'
    var b = a
    b = "good"
    console.log(a) // hello men
    console.log(b) // good
    

    当b改变时,不会影响初始变量a

    2.复合类型
    如数组、对象等,js就会做引用,原对象和拷贝对象会做关联

    var arr1 = [1, 2, 3, 4]
    var arr2 = arr1
    arr2[4] = 9
    console.log(arr1)  // [1,2,3,4,9]
    console.log(arr2)  // [1,2,3,4,9]
    

    arr2改变时arr1也随之改变,这里举了数组的例子,对象也类似。

    浅与深的区别

    为了让拷贝对象 和 原对象完全脱离关系,我们需要用到浅拷贝、深拷贝这两种方法。

    浅拷贝只能复制根属性,做不到真正的全拷贝。
    深拷贝能实现真正的全拷贝,与原对象分清界限。

    浅拷贝

    我们先从简单的入手,浅浅的拷贝一个数组。

    • 方法1
    var array1 = ['a', 'b', 'c']
    var array2 = array1.slice() // 单个复制数组元素
    array2[3] = 'h'
    console.log(array1) // ['a','b','c']
    console.log(array2) // ['a','b','c', 'h']
    
    • 方法2
    var arr1 = [1, 2, 3]
    var arr2 = []
    var copy = (arr1, arr2) => {
        arr1.forEach((element, index) => {
            arr2[index] = element
        })
        return arr2
    }
    arr2 = copy(arr1, arr2)
    arr2[3] = 6
    console.log(arr2) // [ 1, 2, 3, 6 ]
    console.log(arr1) // [ 1, 2, 3 ]
    

    array2只改变自己,不影响他人,是个好同志。
    现在难度升级,我们在数组里加入了对象。

    var array1 = ['a', 'b', 'c', {
        "name": 'leo'
    }]
    var array2 = array1.slice()
    array2[3].name = 'mark'
    console.log(array1) // ['a','b','c',{name: 'mark'}]
    console.log(array2) // ['a','b','c',{name: 'mark'}]
    

    这时候改变array2,array1也跟着变了,哦NO!

    深拷贝

    现在是该大英雄出场了,“深拷贝!深拷贝!”
    不啰嗦,直接上代码。

    // 创建了 一个带数组和对象的元素 (这还是比较简单的对象结构)
    var array1 = ['a', 'b', 'c', { "name": 'leo'}, [8, 9]]
    var array2 = []
    // 创建拷贝方法 
    var copy = (obj1, obj2) => {
        obj1.forEach((item, index) => {
                   // 判断该索引值是否为数组
            if (obj1[index].constructor === Array) {
                obj2[index] = []
                obj1[index].forEach((subItem, subIndex) => {
                    obj2[index][subIndex] = subItem
                })
                    // 判断该索引值是否为对象
            } else if (obj1[index] && typeof obj1[index] === 'object') {
                obj2[index] = {}
                for (let element in obj1[index]) {
                    obj2[index][element] = obj1[index][element]
                }
                    // 不是对象,说明是属性,直接赋值
            } else {
                obj2[index] = item
            }
        })
        return array2
    }
    
    var array2 = copy(array1, array2)
    // 改变对象的属性值
    array2[3].name = 'mark'
    // 改变数组的值
    array2[4][1] = '5'
    console.log(array2) // [ 'a', 'b', 'c', { name: 'mark' }, [ 8, '5' ] ] 
    console.log(array1) // [ 'a', 'b', 'c', { name: 'leo' }, [ 8, 9 ] ] 
    

    恭喜你,我们向要的都实现了!

    深不可测的深拷贝 (递归)

    有些对象是后台传给我们的,拿到之前不知道里面是什么结构。这种数据着么去拷贝呢?
    是时候展示真正的技术了, 那就是递归
    递,层层递进。归,归去来兮。
    总结下来就是...还是不解释了。
    看代码:

    // 创建一个深层嵌套的对象
    var json1 = {
        "name": "leo",
        "age": 20,
        "child": [{
                "eye": "blue",
                "child": [{
                    "photo": "nice"
                }, {
                    "photo": "beautiful"
                }]
            },
            {
                "eye": "red",
                "child": []
            }
        ]
    }
    
    var json2 = {}
    function copy(obj1, obj2) {
        for (var name in obj1) {
            if (typeof obj1[name] === "object") {
                obj2[name] = (obj1[name].constructor === Array) ? [] : {}
               // 递归时改变当前参数位置,
               // 举例:当前name为child时,copy中的参数被替换为 (obj1.child, obj2.child)
                copy(obj1[name], obj2[name])
            } else {
                obj2[name] = obj1[name]
            }
        }
        return obj2
    }
    
    json2 = copy(json1, json2)
    json2.child[0].eye = 'green'
    console.log(json1) // child: [ { eye: 'blue', child: [Object] }
    console.log(json2) // child: [ { eye: 'green', child: [Object] }
    

    最简单的深拷贝

    先把对象使用JSON.stringify()转为字符串,再赋值给另外一个变量,然后使用JSON.parse()转回来即可。

    let a = {
      a1: 1,
      a2: '2',
      a3: [1, 2, 3, 4, 5, 6],
      a4: {
        deep1: 1,
        deep2: 2
      }
    }
    
    let b = JSON.parse(JSON.stringify(a))
    console.log(b)
    a.a4.deep1 = 99
    console.log(a)  // a的属性值变动了
    console.log(b) // b没变
    

    new

    顺带提下,用new 创建的对象,都是引用原对象的。new常常和构造函数一起出现,用于创建对象的继承关系。
    我们应尽量避免引用类型的直接拷贝,这样会改变原对象的属性,在协同工作时会产生不可预期的错误。
    可以用新的方法Object.create()来创建,或者定义一个空的 F(){} 构造函数做衔接。

    参考: https://www.jianshu.com/p/0d7bd31ccf43

    相关文章

      网友评论

        本文标题:浅复制(浅拷贝)和深复制(深拷贝)

        本文链接:https://www.haomeiwen.com/subject/ocmjnqtx.html