深拷贝和浅拷贝、赋值的区别及其原理

前言

js数据类型分为基本类型和引用类型两种

基本类型有:string,number,boolean,null,undefined,symbol

引用类型有:Function,Array,Object

一、赋值与浅拷贝

1.赋值

 var obj1={
        name:'张三',
        age:18,
        class:['一班']
    }
    var obj2=obj1//进行了赋值的操作
    obj2.name='哈哈'
    obj2.class[0]='二班'
    console.log(obj1,1111)
    console.log(obj2,2222)

 打印结果如下:

 结论:从例子的可以看出赋值后的对象obj2改变,原对象obj1的值也改变.这是因为赋值后的对象obj2赋值的是原对象obj1的栈内存地址,他们指向的是同一个堆内存数据,所以对赋值后的对象obj2对数据进行操作会改变公共的堆内存中的数据,所以原对象的值也改变了。

2.浅拷贝

 var obj1={
        name:'张三',
        age:18,
        class:['一班']
    }
  function qianCopy(obj){
    var obj2={}
    for(var attr in obj){//循环对象的所有属性值
         if(obj.hasOwnProperty(attr)){
             obj2[attr]=obj1[attr]
         }
    }
    return obj2
  }
  var obj3=qianCopy(obj1)
  obj3.name='傻逼'
  obj3.class[0]='九班'
  console.log(obj1,1111)
  console.log(obj3,3333)

打印结果如下:

 

 结论:从结果可以看出obj3改变了基本类型的值name,并没有使原对象obj1的name改变,obj3改变了引用类型的值,导致原对象的值也改变了

赋值和浅拷贝的区别总结:

赋值:就是对原对象的栈内存地址进行复制,但是他们的堆内存数据还是共享的。

浅拷贝:是对原对象的属性值进行精准复制,如果原对象的属性值是基本类型,那就是值的引用,所以浅拷贝后修改基本类型不会修改到原对象的,如果原对象属性值是引用类型,那么就是对引用类型属性值的栈内存的复制,所以修改引用类型属性值的时候会修改到原对象。

因此一般对无引用类型的属性的兑现拷贝的时候使用浅拷贝就行,对复杂对象包含引用类型属性的时候使用深拷贝

二、浅拷贝的实现方式

1.对象浅拷贝Object.assign()

Object.assign() 方法可以把任意多个的源对象自身的可枚举属性拷贝给目标对象,然后返回目标对象。

代码如下(示例):

 var obj1={
        name:'张三',
        age:18,
        class:['一班']
    }
    let obj2=Object.assign({},obj1)
    obj2.class[0]='五班'
    obj2.age=19
    console.log(obj1,111)
    console.log(obj2,222)

 打印结果如下:

 可以看出原对象obj1属性中不包含引用类型的时候等价于深拷贝,因为不包含引用类型的时候是对属性值的拷贝也就是值引用,所以对拷贝的对象改变不会影响到原对象,也就等价于深拷贝

2.数组的浅拷贝…Array.prototype.slice()与Array.prototype.concat()

代码如下(示例):

var obj1 = ['张三', 18,{ class: '一班' }]
    let obj2=obj1.slice()
   obj2[0]='哈哈'
   obj2[2].class='五班'
    console.log(obj1,111)
    console.log(obj2,222)

打印:

 var obj1 = ['张三', 18,{ class: '一班' }]
    let obj2=obj1.concat()
   obj2[0]='哈哈'
   obj2[2].class='五班'
    console.log(obj1,111)
    console.log(obj2,222)

concat方法结果一样。

3.解构 

let aa = {
	age: 18,
	name: 'aaa',
	address: {
		city: 'shanghai'
	}
}
 
let bb = {...aa};
bb.address.city = 'shenzhen';
 
console.log(aa.address.city);  // shenzhen

三、深拷贝的实现方法

1.JSON.parse(JSON.stringify())

var arr = ['jack',25,{hobby:'tennise'}];
        let arr1 = JSON.parse(JSON.stringify(arr))
        arr1[2].hobby='rose'
        arr1[0]='rose'
        console.log( arr[2].hobby) //tennise
        console.log( arr[0]) //jack

缺点:当对象里面有函数的话,深拷贝后,函数会消失

2.手写递归函数实现深拷贝

 var obj = {   //原数据,包含字符串、对象、函数、数组等不同的类型
       name:"test",
       main:{
           a:1,
           b:2
       },
       fn:function(){
          
       },
        friends:[1,2,3,[22,33]]
   }
   function copy(obj){
        let newobj = null; 
        if(typeof(obj) == 'object' && obj !== null){ 
            newobj = obj instanceof Array? [] : {};   
            for(var i in obj){  
                newobj[i] = copy(obj[i])
            }
        }else{
            newobj = obj
        }    
      return newobj;    //函数必须有返回值,否则结构为undefined
   }
 
    var obj2 = copy(obj)
    obj2.name = '修改成功'
    obj2.main.a = 100
   console.log(obj,1111)
   console.log(obj2,2222)

 打印结果:

 

总结

1.赋值时不论基本数据类型还是引用数据类型,当通过赋值得到的新对象改变时,原对象数据都会同步改变。

2.浅拷贝时基本数据类型并不用担心原对象改变,引用类型 才会改变原对象。

3.JSON.parse(JSON.stringify())深拷贝时,注意函数会消失。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值