深拷贝和浅拷贝

本文详细介绍了JavaScript中的深拷贝和浅拷贝概念,包括它们的拷贝层级区别、赋值与浅拷贝的差异。同时,文章列举了多种浅拷贝实现方式,如for/in、展开语法、slice、concat和Object.assign,并重点讨论了深拷贝的实现,如JSON.parse(JSON.stringify())方法的限制和自定义递归函数。此外,还提及了lodash库的_.cloneDeep()方法作为深拷贝的一种选择。

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

深拷贝和浅拷贝是只针对Object和Array这样的引用数据类型的,都是只拷贝对象自身的属性,不对原型属性进行拷贝。

深拷贝和浅拷贝的拷贝层级问题
1)浅拷贝是拷贝一层,深层的数据复制的是内存地址
2)深拷贝是每一层的数据都会拷贝出来,两个对象是完全独立的

赋值和浅拷贝的区别
1)赋值:两个变量保存的是同一个对象的内存地址,指向同一个存储空间,无论通过哪个变量对对象进行改变,通过另一个变量查找到的对象也是改变的
2)浅拷贝是按位拷贝对象,它会创建一个新对象,这个对象有着原始对象属性值的一份精确拷贝。如果属性是基本类型,拷贝的就是基本类型的值,如果属性是内存地址(引用类型),拷贝的是内存地址

浅拷贝的实现
1)Object和Array都能使用的方式:for/in、展开语法
1.1)for/in方式

		  function shallowCloning(obj) {
            // 只拷贝引用数据类型
            if (typeof obj !== "object") return;
            
			let newObj = null;
			if(obj !== null){
				//根据obj的类型判断是新建一个数组还是一个对象
             	newObj = obj instanceof Array ? [] : {};

            	// 遍历obj,并且判断是obj自身的属性才进行拷贝
            	for (const key in obj) {
                	if(obj.hasOwnProperty(key)){
                    	newObj[key] = obj[key];
                	}
            	}
			}
            return newObj;
        }

1.2)展开语法

		  function shallowCloning(obj) {
           	// 只拷贝引用数据类型
            if (typeof obj !== "object") return;
            
			let newObj = null;
			if(obj !== null){
				//根据obj的类型判断是展开一个数组还是展开一个对象
             	newObj = obj instanceof Array ? [...obj] : { ...obj };
			}
            return newObj;
        }

2)Array可以使用的方式:slice方法、concat方法
2.1)slice方法
从数组中截取部分元素组合成新的数组(不会改变原数组),第一个参数:开始截取的位置,第二个参数:结束截取的位置(不包括结束位置),不传递第二个参数时,截取到数组的最后元素

		let arr = [0, 1, 2, 3, 4, 5, 6];
        console.log(arr.slice(1, 3)); // [1,2]

不设置参数时,会获取所有元素

        let arr = [0, 1, 2, 3, 4, 5, 6];
        console.log(arr.slice()); //[0, 1, 2, 3, 4, 5, 6]

2.2)concat方法
concat方法用于连接两个或多个数组,会将新数组的成员,添加到原数组的尾部,然后返回一个新数组,原数组不会改变,元素是值类型的是复制操作,如果是引用类型还是指向同一对象。

		let array = ["hdcms", "houdunren"];
        let hd = [1, 2];
        let cms = [3, 4];
        console.log(array.concat(hd, cms)); //["hdcms", "houdunren", 1, 2, 3, 4]
        console.log(array);//["hdcms", "houdunren"]

3)Object可以使用的方式:Object.assign方法
Object.assign(target,…sources)
//target:目标对象
//source:任意多个源对象
//返回值:目标对象会被返回

该方法可以把任意多个源对象自身的可枚举属性拷贝给目标对象,然后返回目标对象。如果目标对象与源对象有同名属性,或多个源对象有同名属性,则后面的属性会覆盖前面的属性。

		let user = {
            name: '后盾人'
        };
        let hd = {
            stu: Object.assign({}, user)
        };
        hd.stu.name = 'hdcms';
        console.log(user.name);//后盾人

Object.assign方法可以处理数组,但是会把数组视为对象。
下面代码中,Object.assign()把数组视为属性名为 0、1、2 的对象,因此源数组的 0 号属性4覆盖了目标数组的 0 号属性1。

        console.log(Object.assign([1, 2, 3], [4, 5]));//[4,5,3]

深拷贝的实现
1)JSON.parse(JSON.stringify())
原理是:用JSON.stringify()方法将js对象序列化为一个JSON字符串,JSON.parse()方法可以将JSON字符串反序列化为一个js对象。
注意:可以实现数组或对象深拷贝,但是不能处理函数,因为JSON.stringify()方法不能接收函数。

JSON的初步了解:基本结构和数组结构

1.1)JSON.stringify()
JSON.stringify()方法具体使用参见MDN网站

语法:
JSON.stringify(value,replacer,space)
参数说明:
value:必需,要转换的JavaScript值(通常为对象或数组,要求最好用双引号包裹属性)
replacer:可选。用于转换结果的函数(传入参数key和value)或者数组。
space:可选。文本添加缩进、空格和换行符。
返回值:包含JSON文本的字符串。

1.2)JSON.parse()
JSON.parse()方法具体使用参见MDN网站

语法:JSON.parse(text,reviver)
参数说明:
text:必需,一个有效的JSON字符串
reviver:可选。一个用以在返回之前对所得到的对象执行变换(操作)的函数,将为对象的每个成员调用此函数,并传入每个成员的键和值,使用返回值而不是原始值。

2)自己编写函数(递归)

		function deepCloning(obj) {
            if (typeof obj !== "object") return;
            let newObj = null;
            if (obj !== null) {
                newObj = obj instanceof Array ? [] : {};
                for (const key in obj) {
                    if (obj.hasOwnProperty(key)) {
                        newObj[key] = typeof obj[key] === "object" ? deepCloning(obj[key]) : obj[key];
                    }
                }
            }
            return newObj;
        }

3)函数库lodash
该函数库提供_.cloneDeep()方法来做深拷贝(这一点,本人还没有实践过,看别人的博客中写的,先总结下来)

最最最通用版(考虑了循环引用的问题)

function clone(parent) {
    const allParents = [];
    const allChildren = [];

    function _clone(parent) {

        if (parent == null) {
            return null;
        }
        if (typeof parent !== "object") {//如果不是对象直接返回
            return parent;
        }

        const child = parent instanceof Array ? [] : {};

        const index = allParents.indexOf(parent);

        if (index !== -1) {
            return allChildren[index];
        }

        allParents.push(parent);
        allChildren.push(child);
        //child保存的是parent对象深拷贝出来的新对象的内存地址,
        // 这个内存地址是一直不变的,变的是这块内存中保存的内容

        for (const key in parent) {
            if (parent.hasOwnProperty(key)) {
                const value = parent[key];
                child[key] = _clone(value);
            }
        }
        return child;
    }
    return _clone(parent);
}


let obj = {
    site: "baidu.com",
    protocol: [{
        p1: "http",
        p2: "https"
    }]
}
obj.self = obj;

let newobj = clone(obj);
console.log(obj);
console.log(newobj);
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值