JS中的浅拷贝和深拷贝

前言

在js中涉及到深浅拷贝的时候,就会牵扯到

栈和堆

在编译期间,除了声明变量和函数,查找函数中的标识符这两项工作之外,还有内存分配,不同类型的数据会分配到不同的内存空间。

栈内存
举个栗子,就比如有一个书架,每本书对应一个书格,只有先拿出来才能再放进去,是一个先出后进的顺序。这个书架就可以比作栈内存。

  • 栈内存中主要保存基本数据类型值和引用数据类型值的地址。
  • 栈内存是存在大小限制和储存上限的,就是一简单存储。
  • 栈内存是自动分配内存大小的,读取数据快,写入速度快,但是储存内容少,变量一旦不使用系统会自动清除掉释放内存。

堆内存
堆内存就好比一个百宝箱,这个时候我们不需要先出后进的操作,只需要知道我们想要什么东西,根据这个东西的名称直接去拿就行了。

  • 主要保存一组无序且唯一的引用数据类型值,可以使用栈内存中的键名来获取。
  • 堆内存的存储数据多,读写速度慢,一般对象会储存在堆中,对内存的大小和限制都是未知的。
  • 堆内存是动态分配内存的,也不一定会自动释放内存。
    在这里插入图片描述

赋值和赋址

先来简单说一下
赋值指的就是把堆内存中的对象数据获取到存放到栈内存中,就相当于在栈内存中新创建了一个值然后把该值赋给新创建的变量;

赋址指的就是把堆内存的数据值存放到栈内存中,并且地址也是指向堆内存的,就相当于在栈内存中创建一个指针,这个指针指向堆内存中的值。

看段代码:

function a(){
    var a=[1,2,3,4,5];	//在堆内存中存储一个arry
    var s=a;			//赋址操作,在栈中创建一个叫s的指针指向堆内存中的a值
    var d=a[2];			//赋值操作,在栈中创建一个新值,并把堆中a数组的某个值赋值给栈中的新值
    console.log(s);		//[1,2,3,4,5]
    console.log(d);		//3

    s[0]=6;				//改变的是堆中a的值,因为s是指向a的
    d=7;				//无法改变a中对应的值,因为d是在栈中新建的一个变量,这是它已经和堆没有关联了
    console.log(s);		//[6,2,3,4,5]
    console.log(d);		//7
    console.log(a);		//[6,2,3,4,5]
}

从上面的代码我们可以知道,改变了s的数据,同时会改变堆中a的对应值;改变d的值,堆中a的对应值是不会做出改变的。

这就是赋值和赋址的区别。

浅拷贝和深拷贝

所谓拷贝,就是赋值的意思;把一个值赋值给另一个值。

基本数据类型赋值的时候,赋值的是数据,所以并不存在浅拷贝和深拷贝的问题。

浅拷贝

创建一个新的对象值,给这个对象赋原始对象的属性值,指向不同的地址。也就是说,浅拷贝是拷贝的原始对象的属性值,如果拷贝的这个值是引用类型,拷贝的就是原始对象的地址,拷贝的不深所以叫浅拷贝。

  • 拷贝原对象中的基本数据类型值,是不会改变原对象的。
  • 如果拷贝的是原对象中子对象的引用数据类型值,是会改变原对象的值;因为在堆中,对象里面的子对象会和主对象共享地址,这个时候原对象也会跟着改变。

栗子:

1,使用for…in…实现浅拷贝

// 使用for...in..实现浅拷贝
function s(){
    // 原对象,【堆中】
    var _data={
        id:1,
        num:10,
        list:{
            id:1,
            num:1
        }
    };

    var _newData={};    //新创建一个对象,【栈中】

    // 使用for...in...进行拷贝操作
    for(var i in _data){
        _newData[i]=_data[i];
    }

    // 对浅拷贝的新对象做操作
    _newData.id=2;
    _newData.list.id=2;

    console.log('_data值:',_data)
    console.log('_newData值:',_newData)

    // 输出
    // _data值:,{"id":1,"num":10,"list":{"id":2,"num":1}}
    // _newData值:,{"id":2,"num":10,"list":{"id":2,"num":1}}
}

从面的栗子可以很清楚的看到,浅拷贝确实是满足我们上面说的两个小点。

2,使用Object.assign()实现浅拷贝

// 使用Object.assign()实现浅拷贝
function d(){
    // 原对象,【堆中】
    var _data={
        id:1,
        num:10,
        list:{
            id:1,
            num:1
        }
    };

    var _newData={};    //新创建一个对象,【栈中】

    // 使用Object.assign(新对象,需要拷贝的对象)进行拷贝操作
    Object.assign(_newData,_data)

    // 对浅拷贝的新对象做操作
    _newData.id=2;
    _newData.list.id=2;

    console.log('_data值:',_data)
    console.log('_newData值:',_newData)

    // 输出
    // _data值:,{"id":1,"num":10,"list":{"id":2,"num":1}}
    // _newData值:,{"id":2,"num":10,"list":{"id":2,"num":1}}
}

这个方法相对于for…in…简单很多。

深拷贝

深拷贝不仅仅只是把原对象赋值给新对象,并且还会把原对象中子对象都原模原样赋值给新对象,这样修改新对象后就不会改变原对象的值。

栗子:

1,使用JSON.stringify()实现深拷贝

需要注意的是这种方法无法拷贝,正则表达式,undefined,function

// 使用JSON.stringify()进行深拷贝
function f(){
    // 原对象,【堆中】
    var _data={
        id:1,
        num:10,
        list:{
            id:1,
            num:1
        }
    };

    var _newDataStr=JSON.stringify(_data),
        _newData=JSON.parse(_newDataStr);

     // 对浅拷贝的新对象做操作
    _newData.id=2;
    _newData.list.id=2;

    console.log('_data值:',_data)
    console.log('_newData值:',_newData)  

    // 输出
    // _data值:,{"id":1,"num":10,"list":{"id":1,"num":1}}
    // _newData值:,{"id":2,"num":10,"list":{"id":2,"num":1}} 
}

2,使用递归的方式实现深拷贝

// 使用递归的方式进行深拷贝
function g(data) {
    // 首先判断data是基本数据类型还是引用数据类型,如果是基本直接返回
    if (typeof data != "object") {
        return data
    }

    // 根据是数组还是对象进行新建变量
    var _newData = Array.isArray(data) ? [] : {};

    // 判断是数组还是对象
    if (data instanceof Array) {
        console.log("是数组")
        // 数组
        for (var i = 0; i < data.length; i++) {
            _newData[i] = data[i]
            if (typeof _newData[i] == 'object') {
                g(_newData[i]);
            }
        }
    } else {
        console.log("是对象")
        // 对象
        for (var i in data) {
            // 判断对象里面是否有数组或者对象
            if (typeof data[i] == 'object') {
                _newData[i] = g(data[i])
            } else {
                _newData[i] = data[i]
            }
        }
    }
    return _newData
}
console.log(g({ a: 1, s: 2, d: [1, 2, 3] }));		//{"a":1,"s":2,"d":[1,2,3]}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值