JS 中引用类型的引用赋值、浅拷贝和深拷贝

本文深入探讨了JavaScript中对象的引用赋值、浅拷贝和深拷贝的区别及实现方式,包括Object.assign、$.extend、JSON.parse(JSON.stringify())以及递归实现深拷贝的方法,并分析了各种拷贝方式的特点与适用场景。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

var obj = {
    name:'Lee',
    hobby:['吃','睡']
}

请添加图片描述

引用赋值:

赋值得到的对象和原对象引用的是堆内存中的同一个地址,指向的是同一个对象。所以改变赋值后对象的基本类型值和引用类型值,原对象的基本类型值和引用类型值也会被改变。

var obj1 = obj
obj1.name = 'Mary'
obj1.hobby[1] = '买'
console.log(obj)

改变 obj1 的 name 属性和 hobby 属性的第一个元素,可以看到 obj 的值也被改变。
在这里插入图片描述
请添加图片描述

浅拷贝:

浅拷贝得到的对象是重新创建的新对象,但浅拷贝只复制了对象的一层属性。所以改变浅拷贝后对象的基本类型值,原对象的基本类型值不会被改变;改变浅拷贝后对象的引用类型值,原对象的基本类型值就会被改变。

  1. Object.assign():用于将所有可枚举属性的值从一个或多个源对象复制到目标对象。它将返回目标对象。
    var obj2 = {}
    Object.assign(obj2, obj)
    obj2.name = 'Mary'
    obj2.hobby[1] = '买'
    console.log(obj)
    
  2. 展开运算符:
    var obj2 = {...obj}
    obj2.name = 'Mary'
    obj2.hobby[1] = '买'
    console.log(obj)
    
  3. 数组的 contact()slice() 方法都能执行浅拷贝,因为都会返回一个新数组。

改变 obj2 的 name 属性和 hobby 属性的第一个元素,可以看到 obj 的 name 属性值没有被改变,hobby 属性的第一个元素值被改变。
在这里插入图片描述
请添加图片描述

深拷贝:

深拷贝得到的对象是重新创建的新对象。深拷贝会递归复制所有层级,会对对象以及对象的所有子对象进行拷贝。所以改变赋值后对象的基本类型值和引用类型值,原对象的基本类型值和引用类型值都不会被改变。

深拷贝会带来性能上的问题。

  1. 通过 jQuery 中的 $.extend() 方法:用于将一个或多个对象的内容合并到目标对象。

    $.extend( deep, target, object1,…objectN );
    参数:

    1. deep:是否深度合并对象,默认为 false。第一个参数不支持传递 false。
    2. target:目标对象,其他对象的成员属性将被附加到该对象上。
    3. object1:被合并的对象。
    var obj3 = {}
    $.extend(true, obj3, obj)
    obj3.name = 'Mary'
    obj3.hobby[1] = '买'
    console.log(obj)
    
  2. 通过 JSON:利用 JSON.stringify() 将 JS 对象序列化成 JSON 字符串,再使用 JSON.parse() 来反序列化还原 JS 对象。

    
    var obj3 = JSON.parse(JSON.stringify(obj))
    obj3.name = 'Mary'
    obj3.hobby[1] = '买'
    console.log(obj)
    

    在这里插入图片描述
    JSON.parse(JSON.stringify()) 的缺陷:

    • 如果对象中有 undefined、函数和 Symbol,那么 JSON.parse(JSON.stringify()) 后,undefined、函数和 Symbol 将丢失。
    • 如果对象中有 NaN、Infinity 和 -Infinity,那么 JSON.parse(JSON.stringify()) 后都会变成 null。
    • 如果对象中有时间对象,那么 JSON.parse(JSON.stringify()) 后,时间对象将变成字符串的形式。
    • 如果对象中有 RegExp、Error 对象,那么 JSON.parse(JSON.stringify()) 后将只得到空对象。
    • 如果存在循环引用,即对象中有个属性又指向对象自身,那么 JSON.parse(JSON.stringify()) 将会报错。
    const originObj = {
    	name: 'Lee',
    	age: 18,
    	sex: undefined,
    	func: function(){
    		return 'Hello'
    	},
    	[Symbol()]: 'abc',
    	max: Infinity,
    	min: NaN,
    	date: new Date(1536627600000),
    	reg: new RegExp('\\w+'),
    	error: new Error('错误'),
    }
    console.log(originObj)
    const targetObj = JSON.parse(JSON.stringify(originObj))
    console.log(targetObj)
    
    

    请添加图片描述

  3. 递归实现深拷贝:

    // 判断一个值是否是对象
    function isObject(value) {
    	const valueType = typeof value
    	return value !== null && (valueType === 'object' || valueType === 'function')
    }
    
    // 定义深拷贝函数:没有考虑 Set、Map、Symbol、Date、RegExp、Error 等类型、没有考虑循环引用的问题
    function deepCopy(originValue) {
    	// 1. 如果是基本类型值,直接返回
    	if (!isObject(originValue)) {
    		return originValue
    	}
    
    	// 2. 如果是引用类型值,判断具体的类型,创建一个具体的新对象
    
    	// 2.1 如果是函数类型,函数是用来执行的,不需要进行深拷贝
    	if (typeof originValue === 'function') {
    		return originValue
    	}
    
    	// 2.2 如果是数组类型或者对象类型
    	let targetValue = Array.isArray(originValue) ? [] : {}
    	// 2.2.1 遍历每一项,重复深拷贝操作。如果属性值是基本类型值,直接返回;如果属性值是引用类型值,创建一个新对象并遍历重复深拷贝操作
    	for (const key in originValue) {
    		targetValue[key] = deepCopy(originValue[key])
    	}
    	return targetValue
    }
    

    在这里插入图片描述

请添加图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值