浅拷贝与深拷贝的区别(含深拷贝实现方法)

概念及区别

浅拷贝

浅拷贝,按照字面意思来理解,就是浅层拷贝(复制),浅拷贝主要是将拷贝对象的值进行拷贝,当拷贝对象是引用类型是,会直接拷贝对象的引用地址。

深拷贝

深拷贝,就是深层拷贝,当拷贝对象属于引用类型时,深拷贝会层层进入对象,复制其中的值,而不是直接复制引用地址。

区别

浅拷贝与深拷贝的主要区别就在于,当拷贝对象属于引用类型时,

如果使用使用浅拷贝,那么当原对象发生改变时,复制对象也会同时发生改变:

let originData = { a: 1, b: 2}
let copyData = originData // copyData={a: 1,b: 2}
originData.a = 3 // originData = {a: 3, b: 2}    copyData= {a: 3, b: 2}

当使用深拷贝时,复制对象和原对象已经属于两个独立的对象,互相各不影响,所以不会发生改变

let originData = { a: 1, b: 2}
let copyData = originData // copyData={a: 1,b: 2}
originData.a = 3 // originData = {a: 3, b: 2}    copyData= {a: 1,b: 2}

可以参考c语言中的指针,使用浅拷贝时,复制对象只是创建了一个指针,并且将其指向了复制对象的地址,即两个对象同时指向了一个地址,所以当对象数据发生改变时,两个对象都会同时发生改变

使用深拷贝时,也是创建了一个指针,但不同的是,深拷贝重新分配了一个存储空间,并且将该指针指向新的存储空间,然后将复制对象中的值,一一复制到新的空间中,完成后,两个对象即是两个完全独立的存储空间,所以当复制对象发生改变时,新的对象并不会随之改变。

深拷贝的方法

JSON.stringify(parse)

该方法只能用于数组以及对象的深拷贝,对function不适用。该方法通过将对象转化为字符串,然后再将字符串转化为对象,从而达到建立新的一个完全独立的对象的目的。

但是该方法存在较大的弊端,只有在源数据格式相对简单的情况下才可以使用,但源数据中包括Map,Set,Date以及其他内置类型时,会存在数据类型丢失的情况

let target = {a: 1, b:2}
// JSON.stringify(target): '{a: 1,b:2}'字符串
// JSON.parse(JSON.stringify(target)): {a:1,b:2}
let copy = JSON.parse(JSON.stringify(target)) // copy = {a:1, b:2}

let a = {}
let b = new Set()
b.add(11)
// 此时b为Set类型数据
a.b = b
console.log(a) // {b: Set(10)}
console.log(JSON.parse(JSON.stringify(a))) // {b: [10]} 数据类型丢失

$.extend

利用$.extend也可以实现深拷贝,但是需要引入jquery库,如果项目中并没有引入JQ库,那么明显是不合适的。

let a = [1,2,3]
let b = a
a.push(4)
console.log(a) // [1,2,3,4]
console.log(b) // [1,2,3,4]

let a = [1,2,3]
let b = $.extend(true, [],a)
a.push(4)
console.log(a) // [1,2,3,4]
console.log(b) // [1,2,3]

递归实现深度克隆

递归实现深度克隆,需要判断节点是否为简单数据类型,以及节点为引用数据类型时,该如何处理。

当节点为引用数据类型时,遍历该节点,如果子节点为简单数据类型,直接进行简单复制,如果为引用数据类型,就递归进入该子节点,重复操作。并且在末尾返回该子节点的克隆节点。

function deepClone(obj){
    let objClone;
    // 判断源数据是否为引用数据类型
    if(obj && typeof obj==="object"){
    	objClone = Array.isArray(obj)?[]:{}
    	for(key in obj){
    		// 判断子节点是否为对象
    		if(obj.hasOwnProperty(key)){
    			if(obj[key]&&typeof obj[key] ==="object"){
					objClone[key] = deepClone(obj[key]);
                }else{
                	// 如果不是,简单复制
                    objClone[key] = obj[key];
                }
            }
        }
    }else{
    	objClone = obj
    }
    return objClone;
}

误区

误区1(Array.slice)

Array.slice并不能实现完全的深度拷贝,其只进行了一层的拷贝,当源数据拥有多层结构时,第二层及之后的节点,依旧会受源数据影响。

let a = [
	[1,2,3],
	[2,3,4]
]
let b = a.slice()
a[1][2] = 5
console.log(a) // [[1,2,3],[2,3,5]]
console.log(b) // [[1,2,3],[2,3,5]]

误区2(Array.concat)

因为Array.concat方法返回了一个新的数组,所以很容易导致开发者认为这是一个深拷贝,但非常的遗憾,这同样只在第一层进行了拷贝。

let a = [
	[1,2,3],
	[2,3,4]
]
let b = a.concat([])
a[1][2] = 5
console.log(a) // [[1,2,3],[2,3,5]]
console.log(b) // [[1,2,3],[2,3,5]]
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值