js深拷贝与浅拷贝

news2024/10/5 17:24:13

 1.浅拷贝概念

浅拷贝是其属性与拷贝源对象的属性共享相同引用,当你更改源或副本时,也可能(可能说的是只针对引用数据类型)导致其他对象也发生更改。

特性

  • 会新创建一个对象,即obj===obj2返回fasle;
  • 对象中如果是基本数据类型会单独开辟一份空间存储,如果是引用数据类型则共用一个内存地址。即如果是基本数据类型新对象修改不会造成源对象值改变,但是如果是引用数据类型,修改会造成源对象属性同时被修改

2.var people1 = people不是浅拷贝

注意var people1 = people不是浅拷贝。只是对内存地址进行赋值,people1 === people 返回true,这种不是浅拷贝

// 这个种方式不是浅拷贝
var people = {
    name: 'allen'
}

// 这里只是对内存地址进行赋值,people1 === people 返回true,这种不是浅拷贝
var people1 = people
people1.name = 'jason'

3.浅拷贝示例

浅拷贝:会新创建一个对象,对源对象的属性进行拷贝,使两者共享同一个地址。obj === obj2返回false。比如:var obj2 = Object.assign({}, obj)

说明:Object.assign()是浅拷贝,会生成一个新的对象浅拷贝对于基本数据类型a,会重新单独开辟一份空间进行存储, 而对于引用数据类型b拷贝的是其内存地址,所以obj2和obj对于拷贝后的b是共用一份内存地址。所以如果修改obj2中b的b1的值,则obj中的b1的值也会改变

4.浅拷贝的方法(Object.create(),Object.assign(),扩展运算符)

4.1Object.create()

创建一个对象,会将源对象放到新对象的原型上(即会将obj对象放到obj2的原型上),返回一个新的对象。

var obj = {a: 1}
var obj2 = Object.create(obj)
obj.a = 2 //修改基本数据类型,源对象也会改变
console.log(obj === obj2); //false
console.log(obj,obj2);

4.2 Object.assign()

将对象属性进行合并,并返回新的对象,遇到重名对象会进行覆盖

var obj = {
    a: 1,
    b: {
        b1: 1
    }
}
var obj2 = Object.assign({}, obj)
obj.a = 2
obj.b.b1 = 2

console.log(obj === obj2);
console.log(obj, obj2);

结果:源对象基本数据类型值没有改变,引用数据用的同一个会改变

4.3扩展运算符

var obj = {
    a: 1,
    b: {
        b1: 1
    }
}

var obj2 = {...obj};
obj2.a = 3;
obj2.b.b1 = 2;

console.log(obj === obj2);
console.log(obj, obj2);

 结果:同上

5.深拷贝概念

深拷贝是指其属性与其拷贝的源对象的属性不共享相同的引用,当你更改源或副本时,可以确保不会导致其他对象也发生更改

6.深拷贝的方法

6.1JSON.parse(JSON.stringify())

原理:通过改变数据类型的形式进行深拷贝,JSON.stringify()将对象序列化成字符串,而JSON.parse()将字符串转为对象。注意不是JSON.stringify()是两个方法组合使用

var obj = {
    a: 1,
    b: {
        b1: 1
    }
}
var obj1 = JSON.parse(JSON.stringify(obj))
obj1.a = 2
obj1.b.b1 = 2
console.log(obj === obj2);
console.log(obj, obj2);

结果:基本数据类型和引用数据类型的改变都不会印象源对象 

6.2 Lodash.cloneDeep()

cdn地址 : https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.21/lodash.min.js

import _ form "lodash";

var obj = {
    a: 1,
    b: {
        b1: 1
    }
}
var obj1 = _.cloneDeep(obj)
obj1.a = 2
obj1.b.b1 = 2

7.深拷贝的原理

7.1主要考虑以下三个方面:

  1. 数据类型的划分
  2. 递归处理
  3. 循环引入的处理:循环引入即对象可以一直展开,无限嵌套,无限循环

7.1循环引入:

var obj = {};
obj.a = obj;
console.log(obj);

对象可以一直展开,无限嵌套,无限循环

 8.手动实现深拷贝

 8.1方式一:简单实现(只判断数据基本类型和针对对象数据结构)

  • 判断数据类型,特殊类型直接返回;
  • 遍历对象进行递归处理; 
        function deepClone(obj) {
            console.log(typeof obj);

            // 判断如果为null,不等于字符串的object,function, 为Date类型,RegRex类型则直接返回
            if (obj === null || typeof obj !== 'object' || typeof obj === 'function' || obj instanceof Date || obj instanceof RegExp) {
                return obj;
            }

            if (obj instanceof Object) {
                const newObj = {};
                for (let key in obj) {
                    newObj[key] = deepClone(obj[key]);
                }
                // 注意是最后return不是在for循环中returnnewObj[key]
                return newObj;
            }
        }

        var obj = {
            a: 1,
            b: {
                b1: 1
            }
        }
        var obj2 = deepClone(obj);
        obj2.a = 2;
        obj2.b.b1 = 2
        console.log(obj === obj2);
        console.log(obj, obj2);

 

8.2 方式二(增强): 针对Map、Set、Array等结构进行深拷贝处理

  •  判断数据类型,特殊类型直接返回;
  • 遍历对象进行递归处理; 
  • 对Map,Set,Array数据结构进行处理
  • 注意Map,Set,Array各自不同遍历及修改的方法
        function deepClone(obj) {
            // 判断如果为null,不等于字符串的object,function, 为Date类型,RegRex类型则直接返回
            if (obj === null || typeof obj !== 'object' || typeof obj === 'function' || obj instanceof Date || obj instanceof RegExp) {
                return obj;
            }

            if (obj instanceof Set) { //对于Set数据结构(for of 循环;newSet.add(val)) 
                const newObj = new Set();
                for (let val of obj) {
                    newObj.add(deepClone(val));
                }
                return newObj;
            } else if (obj instanceof Map) { //对于Map数据结构(for of循环 map.set(key,val))
                const newObj = new Map();
                for (let [key, val] of obj) {
                    newObj.set(key, deepClone(val));
                }
                return newObj;
            } else if (obj instanceof Array) { //对于数组数据结构(使用forEach进行循环push进行添加)
                const newObj = [];
                obj.forEach(item => {
                    newObj.push(deepClone(newObj[item]));
                });
                return newObj
            } else if (obj instanceof Object) {
                const newObj = {};
                for (let key in obj) {
                    newObj[key] = deepClone(obj[key]);
                }
                // 注意是最后return不是在for循环中returnnewObj[key]
                return newObj;
            } else {
                return obj;
            }
        }

        var newMap = new Map();
        newMap.set('key1', 12);
        newMap.set('key2', 13);

        var newSet = new Set();
        newSet.add('11');
        newSet.add('22');

        var obj = {
            a: 1,
            b: {
                b1: 1
            },
            c: {
                c1: [1, 2, 3]
            },
            d: newMap,
            f: newSet
        }
        var obj2 = deepClone(obj);
        obj2.a = 2;
        // 基本数据类型修改值
        obj2.b.b1 = 2
        // 数组改变值
        obj2.c.c1 = [2, 3, 4];
        // map集合修改key1
        obj2.d.set('key1','更改后的key1');
        // Set集合增加一个
        obj2.f.add('set集合增加的值');
        console.log(obj === obj2);
        console.log(obj, obj2);

8.3 方式三(循环引用的处理)

  •  判断数据类型,特殊类型直接返回;
  • 遍历对象进行递归处理; 
  • 对Map,Set,Array数据结构进行处理
  • 注意Map,Set,Array各自不同遍历及修改的方法

如果有循环引用,使用方式二深拷贝处理,会一直循环导致内存溢出。map = new WeakMap()用于判断是否已经有过相同引用

        function deepClone(obj, map = new WeakMap()) {
            // 判断如果为null,不等于字符串的object,function, 为Date类型,RegRex类型则直接返回
            if (obj === null || typeof obj !== 'object' || typeof obj === 'function' || obj instanceof Date || obj instanceof RegExp) {
                return obj;
            }

            // 循环引入处理(如果obj下的key的值为obj本身,则循环将obj的值设置到map的key中)
            // WeakMap的key只能是对象,Set、Map、Array、Object每个都设置map.set(obj,newObj);是因为每种数据结构都有可能存在循环引用
            if (map.get(obj)) {
                return map.get(obj);
            }

            if (obj instanceof Set) { //对于Set数据结构(for of 循环;newSet.add(val)) 
                const newObj = new Set();
                map.set(obj, newObj);
                for (let val of obj) {
                    newObj.add(deepClone(val, map));
                }
                return newObj;
            } else if (obj instanceof Map) { //对于Map数据结构(for of循环 map.set(key,val))
                const newObj = new Map();
                map.set(obj, newObj);
                for (let [key, val] of obj) {
                    newObj.set(key, deepClone(val, map));
                }
                return newObj;
            } else if (obj instanceof Array) { //对于数组数据结构(使用forEach进行循环push进行添加)
                const newObj = [];
                map.set(obj, newObj);
                obj.forEach(item => {
                    newObj.push(deepClone(newObj[item], map));
                });
                return newObj
            } else if (obj instanceof Object) {
                const newObj = {};
                map.set(obj, newObj); //此处设置后,后面newObj即使更改,map内存地址使用的是一个所以map的值也会跟着变
                for (let key in obj) {
                    newObj[key] = deepClone(obj[key], map);
                }
                // 注意是最后return不是在for循环中returnnewObj[key]
                return newObj;
            } else {
                return obj;
            }
        }


        // 循环引入
        var obj3 = {};
        obj3.a = obj3;
        var obj4 = deepClone(obj3);
        obj4.b = obj4;
        console.log(obj3 === obj4);
        console.log(obj3, obj4);

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1091424.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【C++项目】高并发内存池第一讲(项目整体框架介绍、哈系统结构设计)

高并发内存池项目第一讲 一、高并内存池概念二、项目介绍三、项目细节四.哈系统结构设计 一、高并内存池概念 内存池(Memory Pool) 是一种动态内存分配与管理技术。 通常情况下,程序员习惯直接使用 new、delete、malloc、free 等API申请分配和释放内存,…

闭包及底层原理

1.闭包概念 定义:能够访问到其他函数作用域中的对象的函数,称为闭包 误区:闭包不是函数里面嵌套函数 2.闭包的两种写法 2.1函数嵌套写法 // 闭包写法1: 内部嵌套函数function fn(){var a 1;function fn2(){console.log(a1);}fn2();}fn()…

在线兴趣教学类线上学习APP应用开发部署程序组建研发团队需要准备什么?

哈哈哈,同学们,我又来了,这个问题最近问的人有点多,但是说实话我也不知道,但是我还是总结了一下,毕竟我懂点代码的皮毛,同时我检索内容的时候,都是一些没有很新鲜的文案,…

Vue中调用组件使用kebab-case(短横线)命名法和使用大驼峰的区别

文章目录 Vue中调用组件使用kebab-case(短横线)命名法和使用大驼峰的区别1.解析官网手册说明2.什么是“字符串模版”,什么是“dom模版” Vue中调用组件使用kebab-case(短横线)命名法和使用大驼峰的区别 1.解析官网手册…

SSM - Springboot - MyBatis-Plus 全栈体系(二十五)

第五章 SSM 三、《任务列表案例》前端程序搭建和运行 1. 整合案例介绍和接口分析 1.1 案例功能预览 1.2 接口分析 1.2.1 学习计划分页查询 /* 需求说明查询全部数据页数据 请求urischedule/{pageSize}/{currentPage} 请求方式get 响应的json{"code":200,"f…

git pull and git fetch 到底有什么区别?

大家好这里是tony4geek 。 今天给大家介绍git pull and git fetch 有什么区别? 开发过程中大家肯定很多人都用到过git。获取代码有很多的git命令,最长用的命令是pull和fetch。那么问题来了他们之间到底有什么区别,该怎么使用呢?…

C语言之文件操作篇(2)

目录 文件状态指针 文件流 文件的顺序读写 fgetc fputc fgets fputs fscanf fprintf fread fwrite 今天接下来我们讲解文件读写函数。🆗🆗🆗 文件状态指针 文件状态指针也就是文件指示器。(可以理解为光标&#xff09…

【U-Boot笔记整理】U-Boot 完全分析与移植

1. 大纲 大概内容如下: u-boot功能概述 目的功能细分 u-boot源码结构u-boot的配置、编译、连接过程 Makefile深入练习分析u-boot的Makefileu-boot可执行程序的组成 u-boot源码分析 SPL与第1阶段第2阶段核心:命令让u-boot的使用更加便利:env…

python图像检索系统设计与实现 计算机竞赛

0 前言 🔥 优质竞赛项目系列,今天要分享的是 🚩 python图像检索系统设计与实现 🥇学长这里给一个题目综合评分(每项满分5分) 难度系数:3分工作量:3分创新点:4分 该项目较为新颖&#xff0c…

拼多多历史价格数据接口,拼多多商品历史价格接口,拼多多API接口

采集拼多多商品历史价格接口可以采用以下方式: 使用价格监控工具。价格监控工具是一种可以自动监测商品价格变化的工具,它可以帮助消费者快速采集拼多多商品价格信息,还可以提供价格变动趋势的图表分析,使消费者更好地掌握商品价…

Apache_Log4j2查找功能JNDI注入_CVE-2021-44228

Apache_Log4j2查找功能JNDI注入_CVE-2021-44228 文章目录 Apache_Log4j2查找功能JNDI注入_CVE-2021-442281 在线漏洞解读:2 环境搭建3 影响版本:4 漏洞复现4.1 访问页面4.2 poc漏洞验证 4.3 在dnslog获取个域名4.4 使用bp抓包进行分析4.5 通信成功,此处可…

【TA 挖坑04】薄膜干涉 镭射材质 matcap

镭射材质,相对物理的实现? 万物皆可镭射,个性吸睛的材质渲染技术 - 知乎 (zhihu.com) 薄膜干涉材质,matcap更trick的方法?matcapremap, MatCap原理介绍及应用 - 知乎 (zhihu.com) 庄懂的某节课也做了mat…

红队打靶:Nyx: 1打靶思路详解(vulnhub)

目录 写在开头 第一步:主机发现和端口扫描 第二步:ssh私钥登录获取初始立足点 第三步:sudo gcc提权 番外篇:如何掉进web渗透的陷阱无法自拔 总结与思考 写在开头 我个人的打靶顺序是根据红队笔记大佬的视频顺序&#xff0…

最新最全Jmeter+InfluxDB1.8+Grafana可视化性能监控平台搭建(win11本地)

本文前置条件: 1.Jmeter自行部署好,且版本最好要5.4以上; 2.目前InfluxDB最新是V2版本,但与Grafana兼容不太好,且和V1版本的配置连接不一样,本文是InfluxDB1.8版本; 3.介绍的是WIN11本地部署…

《PyTorch深度学习实践》第二讲 线性模型 课后练习

《PyTorch深度学习实践》第二讲 线性模型 课后练习 问题描述代码实现实现效果 问题描述 代码实现 import numpy as np import matplotlib.pyplot as plt from mpl_toolkits.mplot3d import Axes3D# 假设函数为 y 2x 1 x_data [1.0, 2.0, 3.0] y_data [3.0, 5.0, 7.0]# 定义…

【100天精通Python】Day70:Python可视化_绘制不同类型的雷达图,示例+代码

目录 1. 基本雷达图 2. 多组数据的雷达图 3 交互式雷达地图 4 动态雷达图 0 雷达图概述 雷达图(Radar Chart),也被称为蜘蛛图(Spider Chart)或星型图,是一种用于可视化多维数据的图表类型。雷达图通常由…

目标文件格式

目标文件里有什么 目标文件格式 目标文件就是源代码编译后但未进行链接的中间文件(linux下的.o)。 ELF文件:从广义上看,目标文件与可执行文件的格式其实几乎是一样的,可以将目标文件与可执行文件看成是一种类型的文…

【Vue面试题二十】、你有写过自定义指令吗?自定义指令的应用场景有哪些?

文章底部有个人公众号:热爱技术的小郑。主要分享开发知识、学习资料、毕业设计指导等。有兴趣的可以关注一下。为何分享? 踩过的坑没必要让别人在再踩,自己复盘也能加深记忆。利己利人、所谓双赢。 面试官:你有写过自定义指令吗&a…

相似性搜索:第 1 部分- kNN 和倒置文件索引

图片来源:维亚切斯拉夫叶菲莫夫 一、说明 SImilarity 搜索是一个问题,给定一个查询的目标是在所有数据库文档中找到与其最相似的文档。 在数据科学中,相似性搜索经常出现在NLP领域,搜索引擎或推荐系统中,其中需要检索最…

课题学习(七)----粘滑运动的动态算法

一、 粘滑运动的动态算法 在实际钻井过程中,钻柱会出现扭振和粘滑现象(粘滑运动–B站视频连接),但并不总是呈现均匀旋转。如下图所示,提取一段地下数据时,转盘转速保持在100 r/min,钻头转速在0-…