JavaScript的深浅拷贝

什么是深浅拷贝

产生深浅拷贝的原因是JavaScript变量存在两种数据类型的值基本数据类型引用数据类型

而这两种数据类型在内存中的存储方式不同。

  • 基本数据类型:基本数据类型的值会存放在栈中
  • 引用数据类型:引用数据类型的值会存放在堆中,而指向堆位置的地址值会存放在栈中

在这里插入图片描述

基本数据类型的赋值

基本数据类型是将值复制一份给新的变量。

        let a = 10
        let b = a
        console.log(a); //10
        console.log(b); //10
        a = 20
        console.log(a); //20
        console.log(b); //10

引用数据类型的赋值

引用数据类型是将地址值复制一份给新的变量,因此地址值相同,所以指向的堆空间也是相同的。

        let obj1 = {
            a:10,
            arr:['red','yellow']
        }
        let obj2 = obj1
        console.log(obj1);//{a: 10, arr: Array(2)}
        console.log(obj2);//{a: 10, arr: Array(2)}
        obj1.a = 20
        console.log(obj1);//{a: 20, arr: Array(2)}
        console.log(obj2);//{a: 20, arr: Array(2)}

赋值和深浅拷贝的区别

针对引用类型而言:

  • 赋的其实是该对象在栈中的地址值,而不是堆中的数据
  • 浅拷贝:在堆中创建内存,拷贝前后对象中基本数据类型互不影响,但对象中的引用类型共享一块内存,相互影响。
  • 深拷贝:在堆中创建内存,对象中引用类型也是互不影响的。

在这里插入图片描述

浅拷贝的实现方式

说明:当拷贝的对象只有一层基本类型的属性时,下面方法都是深拷贝

1、Object.assign()

Object.assign()可以将多个对象拷贝到目标对象。

        // Object.assign
        let obj1 = {
            name: 'hwm',
            arr: [1, 2, 3]
        }
        // 浅拷贝
        let obj2 = Object.assign({}, obj1)
        obj1.name = 'hwm1'
        obj1.arr.push('4')
        console.log(obj2);//arr: (4) [1, 2, 3, '4'] name: "hwm"

2、Array.prototype.slice()

slice()方法用于截取数组

        // slice
        let arr1 = [1,2,{name:'hwm'}]
        let arr2 = arr1.slice(0)
        arr1[1] = 0
        arr1[2].name = 'abc'
        console.log(arr2); // [1, 2, {name: 'abc'}]

3、Array.prototype.concat()

concat()方法用于合并数组

        // concat
        let arr1 = [1,2,{name:'hwm'}]
        let arr2 = arr1.concat()
        arr1[0] = 0
        arr1[2].name = 'abc' 
        console.log(arr2);// [1, 2, {name: 'abc'}]

4、函数库lodash的_.clone()

var _ = require('lodash');
var obj1 = {
    a: 1,
    b: { f: { g: 1 } },
    c: [1, 2, 3]
};
var obj2 = _.clone(obj1);
console.log(obj1.b.f === obj2.b.f);// true

5、展开运算符 …

let obj1 = { name: 'Kobe', address:{x:100,y:100}}
let obj2= {... obj1}
obj1.address.x = 200;
obj1.name = 'wade'
console.log('obj2',obj2) // obj2 { name: 'Kobe', address: { x: 200, y: 100 } }

深拷贝的实现方式

1、JSON.parse()和JSON.stringify()

        let obj1 = {
            name:'hwm',
            arr:[1,2,3]
        }
        let obj2 = JSON.parse(JSON.stringify(obj1))
        obj1.name = 'abc'
        obj1.arr.push(4)
        console.log(obj2); //{name: 'hwm', arr: [1, 2, 3]

缺点

  • 如果对象里面有Date对象,则拷贝后是字符串
  • 如果对象里面有RegExp、Error对象,则拷贝后是空对象
  • 如果对象里面有function、undefined对象,则拷贝后会丢失

2、函数库lodash的_.cloneDeep()

var _ = require('lodash');
var obj1 = {
    a: 1,
    b: { f: { g: 1 } },
    c: [1, 2, 3]
};
var obj2 = _.cloneDeep(obj1);
console.log(obj1.b.f === obj2.b.f);// false

3、Jquery.extend()

var $ = require('jquery');
var obj1 = {
    a: 1,
    b: { f: { g: 1 } },
    c: [1, 2, 3]
};
var obj2 = $.extend(true, {}, obj1);
console.log(obj1.b.f === obj2.b.f); // false

4、递归实现深拷贝

思路:

  • 判断数据类型
  • 基本类型------直接复制
  • 引用类型------创建对应的类型,逐一复制内容,如果复制的内容依然是引用类型,再此调用自己。
  • 循环引用问题------利用缓存将复制过的属性保存起来
        // 递归实现深拷贝
        function deepClone(src, cache = []) {
            // cache用于缓存遍历过的属性 
            // 用来解决循环引用
            for (let i = 0; i < cache.length; i++) {
                if (src === cache[i].src) {
                    return cache[i].dist
                }
            }
            // 获取数据类型
            let type = Object.prototype.toString.call(src).slice(8, -1)
            // 返回新的对象
            let dist = null
            // 判断是不是基本数据类型
            if (['Number', 'String', 'Boolean', 'Null', 'Undefined'].includes(type)) {
                return src
            }
            // 判断是否是数组
            else if (type === 'Array') {
                // 创建一个数组
                dist = []
            }
            // 判断是不是对象
            else if (type === 'Object') {
                dist = {}
            }
            // 判断是不是时间对象
            else if (type === 'Date') {
                dist = new Date(src)
            }
            // 保存缓存
            cache.push({ src, dist })
            // 遍历对象的属性/数组的项
            for (let key in src) {
                // 将属性绑定在新对象上,并递归判断当前key的值是什么类型
                if (src.hasOwnProperty(key)) {
                    dist[key] = deepClone(src[key], cache) //传递上一个回调的缓存
                }
            }
            return dist
        }
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值