【前端学习——js篇】4.浅拷贝与深拷贝

具体可见https://github.com/febobo/web-interview

4.浅拷贝与深拷贝

①栈内存与堆内存

栈内存(Stack Memory)

  • 栈内存用于存储基本类型的变量和引用类型的变量引用(即指向堆内存中实际数据的指针)。
  • 当一个函数被调用时,会创建一个称为“执行上下文”的栈帧,其中包含函数的参数、局部变量和返回地址。
  • 栈内存是一个后进先出(LIFO)的数据结构,它在程序执行期间动态地增加和减少内存空间。
  • 变量在栈内存中的分配是自动的,当变量超出作用域时会被自动销毁。
//1.基本数据
整数(例如:let num = 10;)
浮点数(例如:let floatNum = 3.14;)
布尔值(例如:let isTrue = true;)
字符串(例如:let str = "Hello";)
//2.函数调用
function add(a, b) {
    return a + b;
}

let result = add(5, 3); // 函数调用会在栈内存中创建执行上下文

基本数据类型和函数调用的数据存储在栈内存中。
【具体例子】

let a = 10;
let b = a; // 赋值操作
b = 20;
console.log(a); // 10值

在这里插入图片描述

堆内存(Heap Memory)

  • 堆内存用于存储引用类型的数据,如对象和数组。
  • 在堆内存中,数据的大小不固定,可以动态地分配和释放内存。
  • 当在栈内存中创建一个引用类型的变量时,实际的数据存储在堆内存中,而栈内存存储的是对数据的引用。
  • 堆内存的数据需要手动的分配和释放,js引擎通过垃圾回收机制来自动管理堆内存中不在使用的数据,以防止内存泄露。
//1.对象
let person = {
    name: "Alice",
    age: 30
};
//2.数组
let numbers = [1, 2, 3, 4, 5];
//3.动态分配的对象
let user = null; // 声明一个变量
user = {
    name: "Bob",
    age: 25
};
//4.使用构造函数创建对象
function Person(name, age) {
    this.name = name;
    this.age = age;
}

let person1 = new Person("Charlie", 35);

对象、数组和动态分配的对象存储在堆内存中。
【具体例子】

var obj1 = {}
var obj2 = obj1;
obj2.name = "wow";
console.log(obj1.name); // wow

在这里插入图片描述

【总结】

  • 栈内存用于存储基本类型的变量和引用类型的变量的引用,具有自动分配和释放内存的特性。
  • 堆内存用于存储引用类型的数据,具有动态分配和手动释放内存的特性。
②浅拷贝

浅拷贝指的是创建新的数据,这个数据是对原数据属性值的精准拷贝。当属性是基本类型,则拷贝的是基本类型的值;如果是引用类型,则拷贝的是内存地址。即浅拷贝是拷贝一层,深层次的引用类型则是共享地址。

//例1
let obj1 = { name: "Alice", age: [30,21] };
let shallowCopy = Object.assign({}, obj1);

shallowCopy.age[0] = 40;
console.log(obj1.age); // 输出 40,原始对象改变

shallowCopy.name = "Bob";
console.log(obj1.name); // 输出 "Alice",原始对象不会受到影响

在例1中,由于属性name是基本类型,所以shallowCopy直接拷贝的数据,对基本数据值修改不会影响原始数据;而age是数组,则拷贝的是数组的地址,所以对age进行修改,则会影响到原始数据。

JavaScript中,存在浅拷贝的现象有:

  • Object.assign

  • Array.prototype.slice(), Array.prototype.concat()

  • 使用拓展运算符实现的复制

Object.assign

Object.assign()静态方法将一个或者多个源对象中所有可枚举的自有属性复制到目标对象,并返回修改后的目标对象。


//Object.assign
var obj = {
    age: 18,
    nature: ['smart', 'good'],
    names: {
        name1: 'fx',
        name2: 'xka'
    },
    love: function () {
        console.log('fx is a great girl')
    }
}
var newObj = Object.assign({}, obj); //浅拷贝

Array.prototype.concat()

concat() 方法用于合并两个或多个数组。此方法不会更改现有数组,而是返回一个新数组。


//Array.concat
const Arr = ["One", "Two", "Three"]
const fxArrs1 = Arr.concat()
fxArrs1[1] = "hello";
console.log(Arr) // ["One", "Two", "Three"]
console.log(fxArrs1) // ["One", "hello", "Three"]

拓展运算符

扩展运算符(spread) 是三个点(…),它如同 rest 运算,将一个数组转为用逗号分割的参数序列。


const fxArr = ["One", "Two", "Three"]
const fxArrs = [...fxArr]
fxArrs[1] = "love";
console.log(fxArr) // ["One", "Two", "Three"]
console.log(fxArrs) // ["One", "love", "Three"]
③深拷贝

深拷贝开辟一个新的栈,两个对象属性完全相同,但是对应两个不同的地址;对一个对象进行修改,不会影响到另一个对象。

// 使用 Lodash 库中的深拷贝方法
//例1
const _ = require('lodash');

let obj1 = { name: "Alice", nestedObj: { key: "value" } };
let deepCopy = _.cloneDeep(obj1);

deepCopy.nestedObj.key = "new value";
console.log(obj1.nestedObj.key); // 输出 "value",原始对象不受影响

JS中,常见的深拷贝方式有:

  • _.cloneDeep()

  • jQuery.extend()

  • JSON.stringify()

  • 手写循环递归

_.cloneDeep()

_.cloneDeep是lodash库中一个用于实现深拷贝方法,它会递归地赋值一个对象或数组及其所有嵌套对象和数组,从而创建一个完全独立的副本。


//见例1

jQuery.extend()

在 jQuery 中,可以使用 jQuery.extend() 方法来实现对象的深拷贝。这个方法可以用于将一个或多个对象的内容合并到目标对象中,如果目标对象中已经存在相同的属性,则会被覆盖;在某种程度上也可以实现深拷贝。当你将一个空对象作为目标对象,并将需要复制的对象作为参数传递给 jQuery.extend() 方法时,它会创建目标对象的一个深层副本。


let obj1 = { 
    name: "Alice", 
    nestedObj: { 
        key: "value" 
    } 
};

let deepCopy = $.extend(true, {}, obj1);

deepCopy.nestedObj.key = "new value";
console.log(obj1.nestedObj.key); // 输出 "value",原始对象不受影响

JSON.stringify()

JSON.stringify() 方法将一个 JavaScript 对象或值转换为 JSON 字符串,如果指定了一个 replacer 函数,则可以选择性地替换值,或者指定的 replacer 是数组,则可选择性地仅包含数组指定的属性。


    const obj = {
        name:{
            key : "a"
        },
        name1: undefined,
        name3: function () { },
        name4: Symbol('A')
    }
    const obj2 = JSON.parse(JSON.stringify(obj));
    obj.name.key = 'b'
    console.log(obj2); // 输出a

但是这种方式存在弊端,会忽略undefinedsymbol函数

循环递归

    function deepClone(obj, hash = new WeakMap()) {
        if (obj === null) return obj; // 如果是null或者undefined我就不进行拷贝操作
        if (obj instanceof Date) return new Date(obj);
        if (obj instanceof RegExp) return new RegExp(obj);
        // 可能是对象或者普通的值  如果是函数的话是不需要深拷贝
        if (typeof obj !== "object") return obj;
        // 是对象的话就要进行深拷贝
        if (hash.get(obj)) return hash.get(obj);
        let cloneObj = new obj.constructor();
        // 找到的是所属类原型上的constructor,而原型上的 constructor指向的是当前类本身
        hash.set(obj, cloneObj);
        for (let key in obj) {
            if (obj.hasOwnProperty(key)) {
                // 实现一个递归拷贝
                cloneObj[key] = deepClone(obj[key], hash);
            }
        }
        return cloneObj;
    }
    const obj = {
        name: {
            key: "a"
        },
        name1: undefined,
        name3: function () { },
        name4: Symbol('A')
    }
    const obj2 = deepClone(obj)
    obj2.name.key = "hello"
    console.log(obj); //输出a
总结

在这里插入图片描述

前提为拷贝类型为引用类型的情况下:

  • 浅拷贝是拷贝一层,属性为对象时,浅拷贝是复制,两个对象指向同一个地址
  • 深拷贝是递归拷贝深层次,属性为对象时,深拷贝是新开栈,两个对象指向不同的地址

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

javaWeb医院在线挂号系统

功能描述 医院挂号系统主要用于实现医院的挂号,前台基本功能包括:用户注册、用户登录、医院查询、挂号、取消挂号、修改个人信息、退出等。 后台基本功能包括:系统管理员登录、医院管理、科室管理、公告管理、退出系统等。 本系统结构如下&…

申请IP地址证书

目录 IP证书的验证条件: 为什么需要申请IP地址证书? 申请IP证书的方法: 注释:IP地址证书也是SSL证书的一种,在验证IP地址所有权后部署于服务器上可实现https访问的一种证书。用公网IP证书可以解决很多问题&#xff…

JavaWeb学习笔记01

一、教程简介 全新JAVAWEB(里程碑版) 一套更适合后端工程师学习的WEB教程 All in Java 1、后端 ① Spring全家桶及微服务框架 ② 高性能数据库和消息组件 ③ Web攻击防护安全控制手段 ④ 其他第三方SDK生态环境 ...... 2、前端 ① 视图三大件&…

构建医疗服务新平台:开发智慧医院系统源码实战教学

本篇文章,小编将深入探讨如何通过开发智慧医院系统源码,构建医疗服务新平台的实战教学。 一、开发准备 在开始开发智慧医院系统之前,我们首先需要明确系统的功能需求和技术实现方案。 二、实战教学 1.系统架构设计 这包括数据库设计、前后…

【Git】日志功能

1. git日志显示 # 显示前3条日志 git log -3# 单行显示 git log --oneline# 图表日志 git log --graph# 显示更改摘要 git log --stat# 显示更改位置 git log --patch 或 git log -p# 查看指定文件的提交历史记录 git log {filename}例子1:单行显示 例子2&#xff…

洛谷_P4995 跳跳!_python写法

P4995 跳跳&#xff01; - 洛谷 | 计算机科学教育新生态 (luogu.com.cn) n int(input()) data list(map(int,input().split())) data.append(0) data.sort()sum 0 l 0 r len(data)-1 flag 1 while l<r:sum (data[l]-data[r])**2if flag:l 1flag 0else:r - 1flag 1…

Wind X98 DM R2蓝牙5.2双模热插拔PCB

键盘使用说明索引&#xff08;均为出厂默认值&#xff09; 一些常见问题解答&#xff08;FAQ&#xff09;注意首次使用步骤蓝牙配对&#xff08;重要&#xff09;蓝牙和USB切换键盘默认层默认触发层0的FN键配置的功能默认功能层1配置的功能默认的快捷键 蓝牙参数蓝牙MAC地址管理…

发现了一本超厉害的英语秘籍,绝对YYDS

昨天冷月小姐姐分享了一本书&#xff0c;她说是一位英语大神发她的。 我也打开了&#xff0c;很酷炫。 群友们也在与时俱进&#xff0c;随手截图&#xff0c;分享了大模型对文档的理解。 你可能会想&#xff0c;关注宏观经济有啥用&#xff0c;自己只是大海中的浪花一朵。 还有…

相交链表:寻找链表的公共节点

目录 一、公共节点 二、题目 三、思路 四、代码 五、代码解析 1.计算长度 2.等长处理 3.判断 六、注意点 1.leetcode的尿性 2.仔细观察样例 3.经验总结 一、公共节点 链表不会像两直线相交一样&#xff0c;相交之后再分开。 由于单链表只有一个next指针&#xff0…

github配置ssh

生成公钥 在电脑用户的目录下打开终端执行 ssh-keygen -t rsa: 执行完不要关 配置文件 看看用户的目录里 .ssh 目录&#xff1a; Host github.comHostname ssh.github.comPort 443配置公钥 复制 id_rsa.pub 文件里的内容 粘贴到 github上 连接密钥 回到刚才的终端…

Windows系统部署Net2FTP网站结合内网穿透轻松打造可公网访问个人云盘

文章目录 1.前言2. Net2FTP网站搭建2.1. Net2FTP下载和安装2.2. Net2FTP网页测试 3. cpolar内网穿透3.1.Cpolar云端设置3.2.Cpolar本地设置 4.公网访问测试5.结语 1.前言 文件传输可以说是互联网最主要的应用之一&#xff0c;特别是智能设备的大面积使用&#xff0c;无论是个人…

【主成分分析(PCA)】

文章目录 一、什么是主成分分析&#xff08;PCA&#xff09;&#xff1f;主成分的选取方差的重要性数据降维 PCA的应用场景 二、主成分分析的工作原理1.方差和数据的重要性2.计算协方差矩阵3.特征值和特征向量4.选择主成分 三、PCA的实现步骤1.标准化数据集2.计算协方差矩阵3.计…

Windows应用商店打不开怎么办?

大家习惯在windows应用商店下载应用和软件,操作也很方便。最近,有用户却称,win10系统上网情况一切正常,但是就是无法打开应用商店,同时还伴随闪退。这该怎么办呢?针对此故障,小编整理好了解决方法,接下来,将和大家分享Windows应用商店打不开怎么办。 操作方法如下: 1…

SpringBoot如何优雅的进行参数校验

一、传统参数校验 虽然往事不堪回首&#xff0c;但还是得回忆一下我们传统参数校验的痛点。 下面是我们传统校验用户名和邮箱是否合法的代码 if (username null || username.isEmpty()) {throw new IllegalArgumentException("用户名不能为空"); }if (isValidEmai…

如何使用PHP和RabbitMQ实现延迟队列(方式一)?

前言 今天我们来做个小试验&#xff0c;用PHP和RabbitMQ实现消息队列的延迟功能。 前期准备&#xff0c;需要安装好docker、docker-compose的运行环境。 需要安装RabbitMQ的可以看下面这篇文章。 如何使用PHP和RabbitMQ实现消息队列&#xff1f;-CSDN博客 一、安装RabbitM…

抖音视频关键词批量采集工具|无水印视频爬虫提取软件

抖音视频关键词批量采集工具&#xff1a; 我们很高兴地介绍最新推出的抖音视频关键词批量采集工具&#xff0c;该工具集成了多项强大功能&#xff0c;让您轻松实现视频内容的批量提取和下载。以下是详细的功能解析和操作说明&#xff1a; 主要功能&#xff1a; 关键词批量提取…

【GIS前沿技术】推荐几款高大上的在线地图,地理学者的福音!

文章目录 一、https://zoom.earth/二、https://www.ventusky.com/三、https://earth.nullschool.net四、https://services.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer?fjsapi 一、https://zoom.earth/ 卫星&#xff1a; 雷达&#xff1a; 降水&#xff…

[深度学习]yolov8+pyqt5搭建精美界面GUI设计源码实现五

【简单介绍】 依托先进的目标检测算法YOLOv8与灵活的PyQt5界面开发框架&#xff0c;我们倾力打造出了一款集直观、易用与功能强大于一体的目标检测GUI界面软件。通过深度融合YOLOv8在目标识别领域的出色性能与PyQt5的精美界面设计&#xff0c;我们成功推出了一款高效且稳定的软…

Element UI中日期选择日(date-picker)等其他选择器下拉显示错位、位置错误解决

省流版 给选择器加上唯一key&#xff08;下面的想看就看&#xff09; 问题复现 需求是用一个下拉切换时间维度的选择&#xff0c;分别为年度、季度、月度&#xff0c;但是开发的时候发现&#xff0c;当切换的时候&#xff0c;视图可正常切换&#xff0c;但点击选择时却发现选…

数据分析面试题(41~50)

41、lstm的原理、lstm和rnn的区别 ①LSTM是一种常用于处理序列数据的循环神经网络&#xff08;RNN&#xff09;架构&#xff0c;特别适用于长序列的建模。其主要特点是通过门控机制来控制信息的流动&#xff0c;从而有效地解决了传统RNN在处理长序列时的梯度消失或爆炸的问题。…