深浅拷贝

JavaScript的数据类型有:

基本数据类型: Number、String、Boolean、null、undefined (栈内存)

引用数据类型: Object (堆内存)

保存在栈内存还是堆内存的根本原因是什么?

根本原因在于保存在栈内存的必须是大小固定的数据,引用类型的大小不固定,只能保存在堆内存中,但是可以把它的地址写在栈内存中以供我们访问

如果是基本数据类型,则按值访问,操作的就是变量保存的值;如果是引用类型的值,我们只是通过保存在变量中的引用类型的地址来操作实际对象

基本类型的复制
var a = 1;
var b = a;//复制
console.log(b)//1
a = 2;//改变a的值
console.log(b)//1

基本类型在复制时,相当于在栈内存中重新开辟了一个内存,存放变量b,因此在修改a时,b和a互不影响

引用类型的复制
var color1 = ['red','green'];
var color2 = color1;//复制
console.log(color2)//['red','green'];
color1.push('black') ;//改变color1的值
console.log(color2)//['red','green','black']

引用类型在复制时,相当于复制的是某个对象的指针(即引用地址),而不复制对象本身。新旧对象还是共享同一块内存,因此当color1更改了之后,color2也会跟着改变。

针对引用类型的拷贝

由上述可知,引用类型复制的是指针,那么如何切断两者之间的关系呢?可以拷贝一份它的数据,根据拷贝的层级又分浅拷贝和深拷贝。

浅拷贝:

  • 浅拷贝只拷贝一层
// 浅拷贝
function shallowClone(source) {
    var target = {}
    for (var i in source) {
        if (source.hasOwnProperty(i)) {
            target[i] = source[i]
        }
    }
    return target
}

var a1 = {b: {c: {d: 1}}}
var a2 = shallowClone(a1)
a1.b.c = 4
console.log(a1)  // { b: { c: 4 } }
console.log(a2)  // { b: { c: 4 } }
  • ES6 的 Object.assign()
  • ES7 的...解构运算符

深拷贝:深拷贝就是能够实现真正意义上的数组和对象的拷贝。递归调用"浅拷贝"。(深拷贝会另外创造一个一模一样的对象,新对象跟原对象不共享内存,修改新对象不会改到原对象)

深拷贝的方式:
  • JSON.parse(JSON.stringify(src)):这种方法有局限性,如果属性值是函数或者一个类的实例的时候,无法正确拷贝
const liLei = {
    name: 'lilei',
    age: 28,
    habits: ['coding', 'hiking', 'running']
}
const liLeiStr = JSON.stringify(liLei)
const liLeiCopy = JSON.parse(liLeiStr)
liLeiCopy.habits.splice(0, 1) 
console.log('李雷副本的habits数组是', liLeiCopy.habits)
console.log('李雷的habits数组是',  liLei.habits)
  • 采用递归的方式
function deepClone(obj) {
    if (typeof obj !== 'object' || obj === null) { // 基本类型本身就是深拷贝
        return obj
    }

    // 定义结果对象
    let copy = {}

    // 定义结果数组
    if (obj.constructor === Array) {
        copy = []
    }

    // 遍历对象的Key
    for (let key in obj) {
        console.log('key', key)
        // 如果key是对象的自有属性
        if(obj.hasOwnProperty(key)) {
            // 递归调用深拷贝方法
            copy[key] = deepClone(obj[key])
        }
    }
    return copy
}

const obj = {
    a: 1,
    b: 2,
    c: 3
}

var a1 = {b: {c: {d: 1}}}
var a2 = deepClone(a1)
a1.b.c = 4
console.log(a1)  // { b: { c: 4 } }
console.log(a2)  // { b: { c: { d: 1 } } }

采用递归的方式调用深拷贝方法,若属性为值类型,则直接返回;若属性为引用类型,则递归遍历。

  • 借助第三方库

    • lodash 的cloneDeep(src)

    • jq 的extend(true, result, src1, src2[ ,src3])

posted @ 2020-12-28 16:16  小蜗蜗蜗牛^o^  阅读(74)  评论(0编辑  收藏  举报