深拷贝与浅拷贝:代码世界里的永恒与瞬间

深夜的咖啡厅里,小美对着屏幕上的代码叹气。

"又出 bug 了?" 小帅递来一杯热拿铁。

"浅拷贝害的。" 小美指着屏幕上的数组:"我复制了一份数据,结果用户修改副本时,原数据也跟着变了......" 小帅凑近看了眼代码:"就像你给我发张照片,我修图后原图也被改了?"

"对!但深拷贝不会。" 小美敲下一行 JSON.parse(JSON.stringify(arr)),"它会彻底复制所有层级的数据,像重新捏一个一模一样的泥人,连指纹都独立。" 小帅若有所思:"所以浅拷贝是瞬间的镜像,深拷贝是永恒的守护?"

小美点头:"就像我们的初心 ------ 代码会变,但数据的灵魂值得被温柔以待。" 以上纯属瞎编,如有雷同,纯属巧合。

在计算机中,不同的数据类型会在不同的内存空间进行存储,

基本数据类型

  • 存储位置 :基本数据类型包括NumberStringBooleanNullUndefinedSymbol(ES6 新增)和BigInt(ES2020 新增),它们的值直接存储在栈内存中。

  • 特点:栈内存是一种自动分配和释放的内存空间,是一种线性的数据结构,具有先进后出的特点。基本数据类型的大小是固定的,所以在栈内存中可以直接存储它们的值,访问速度快。例如:

javascript 复制代码
let num = 10;
let str = 'hello';
let bool = true;

引用数据类型

  • 存储位置 :引用数据类型包括ObjectArrayFunction等。当创建一个引用数据类型的变量时,实际上是在堆内存 中分配一块空间来存储对象的内容,然后在栈内存 中存储该对象在堆内存中的地址 。 即引用数据类型的实例存储在堆内存中,而在栈内存中存储的是指向堆内存中该实例的引用
  • 特点:堆内存是一种用于动态分配内存的区域,其大小不固定,可以根据需要动态地分配和释放。引用数据类型的大小是不固定的,因此需要在堆内存中分配空间来存储其内容
javascript 复制代码
let obj = { name: 'John', age: 30 };
let arr = [1, 2, 3, 4, 5];
function add(a, b) { return a + b; }

浅拷贝

javascript 复制代码
//浅拷贝
let a = 1;
let b = a;
a = 2;
console.log(a);
console.log(b);
  • 原理 :在 JavaScript 中,基本数据类型(像 NumberStringBoolean 等)是按值传递的。当你执行 let b = a 时,实际上是把 a 的值复制给了 b。此后,ab 是相互独立的,对 a 进行修改不会影响 b
  • 输出结果a 的值是 2b 的值依然是 1

深拷贝 - 对于一维数组

javascript 复制代码
//深拷贝  一维数组
let x = [1, 2, 3];
let y = [];
//1、展开运算符
//y = []  堆内存中开辟新的空间
y = [...x];
2、for循环
for(let i = 0;i < x.length;i++){
    y[i] = x[i];
}
x = [111, 222, 333];
console.log(x);
console.log(y);
  • 原理 :此代码运用展开运算符 ... 对数组 x 进行了深拷贝。展开运算符会把数组 x 的元素逐一复制到新数组 y 里。这里的复制是对数组元素的复制,并存放进新的内存空间。当你重新给 x 赋值时,y 不会受到影响。
  • 输出结果x 的值是 [111, 222, 333]y 的值是 [1, 2, 3]

深拷贝 - 多层数组嵌套

javascript 复制代码
//深拷贝  多层数组嵌套
let m = [1, 2, 3, [4, 5,]];
//在堆内存  [1,2,3,0x1234] 第二层还是地址
//直接展开深层次还是存放地址

//1、使用JOSON   但是不适用function
let n = JSON.parse(JSON.stringify(m));
m[3][1] = 1000;
console.log(m);
console.log(n);
  • 原理 :这里借助 JSON.stringify() 把对象 m 转换为 JSON 字符串,再使用 JSON.parse() 把 JSON 字符串转换回对象 n。这样做的效果是递归地复制对象的所有属性,进而实现深拷贝。但要注意,JSON.stringify() 无法处理functionSymbol 等特殊类型。
  • 输出结果m 的值是 [1, 2, 3, [4, 1000]]n 的值是 [1, 2, 3, [4, 5]]

下面举例JSON拷贝引用数据类型中含有函数的方法

深拷贝 - 函数处理(JSON 方式)错误使用

javascript 复制代码
//2、如果是函数
//由于JSON 不能拷贝函数
let j = {
    name: 'zhangsan',
    age: 18,
    fn() {
        console.log(this.name);
    }
}
let k = JSON.parse(JSON.stringify(j));
console.log(j);
console.log(k);
  • 原理 :当使用 JSON.stringify() 处理包含函数的对象时,函数会被忽略。所以,对象 k 里不会有 fn 方法。
  • 输出结果j 包含 nameagefn 方法,而 k 只有 nameage 属性。

浅拷贝 - 函数(一层)

javascript 复制代码
//2、 函数  一层 无嵌套
let o = {
    name: 'zhangsan',
    age: 18,
    fn() {
        console.log(this.name);
    }
}
let p = { ...o };
o.fn = function () {
    console.log(this.age);
}
console.log(o);
console.log(p);
o.fn();
p.fn();
  • 原理 :这里我们回归最初使用展开运算符 ... 对对象 o 进行深拷贝。浅拷贝只复制对象的一层属性,如果属性是数组或者对象,复制的只是地址,所以会有影响。但是如果是函数的话,由于修改方式不同,函数的修啊给i相当于重写了这个函数,所以在堆内存中又会重新开辟一块内存,当修改 ofn 方法时,pfn 方法不受影响。
  • 输出结果ofn 方法会输出 agepfn 方法会输出 name

通过上面的诸多铺垫,接下来介绍手写深拷贝函数。

手写深拷贝函数

javascript 复制代码
function deepClone(data) {
    //函数直接返回,因为函数返回后 会再内存中开辟一个空间
    //object------------> 三种情况    Three sThree scenarios对象 数组 null
    if(typeof data === 'object' && data !== null){
        let res = Array.isArray(data)? [] : {};
        for(let j in data){
            if(data.hasOwnProperty(j)){
                res[j]  = deepClone(data[j]);
            }
        }
        return res;
    }else{
        return data;
    }
}
javascript 复制代码
//示例
let object = {
    name: 'zhangsan',
    age: 18,
    fn() {
        console.log(this.name);
    },
    fn1: function () {
        console.log(this.age);
    },
}

let object2 = deepClone(object);
object.fn1 = function () {
    console.log(this.age + 10);
}
console.log(object);
console.log(object2);
object.fn1();
object2.fn1();
  • 原理deepClone 函数是一个递归函数,用于实现深拷贝。如果传入的数据是对象或数组,就会创建一个新的对象或数组,然后递归地复制每个属性。如果传入的数据是基本数据类型或函数,就直接返回。
  • 输出结果 :修改 objectfn1 方法不会影响 object2fn1 方法,objectfn1 方法会输出 age + 10object2fn1 方法会输出 age

代码的世界里,深拷贝是永恒的承诺,而浅拷贝是短暂的相遇

代码的世界里,没有真正的永恒。但我们依然执着于深拷贝 ------ 就像明知人生无常,却依然相信爱情。愿你在每一次复制中,都能守护住最珍贵的初心,就像deepClone函数里的递归,在层层剥离的温柔里,找到属于自己的永恒。

如果您觉得这篇文章对您有帮助,欢迎点赞和收藏,大家的支持是我继续创作优质内容的动力🌹🌹🌹也希望您能在😉😉😉我的主页 😉😉😉找到更多对您有帮助的内容。

  • 致敬每一位赶路人
相关推荐
夜寒花碎10 分钟前
前端自动化测试一jest基础使用
前端·单元测试·jest
小徐_233314 分钟前
uni-app工程实战:基于vue-i18n和i18n-ally的国际化方案
前端·微信小程序·uni-app
前端小菜鸟一枚s17 分钟前
`ConstantPositionProperty` 的使用与应用
前端·javascript·cesium
JohnsonXin17 分钟前
怎么使用vue3实现一个优雅的不定高虚拟列表
前端·javascript·css·html5
东望18 分钟前
写代码不规范,同事两行泪 😭(工程化如何让团队协作更高效)
javascript·前端工程化
用户8377014088118 分钟前
前端实现个人信息脱敏(手机号、身份证号、姓名、邮箱)JavaScript代码示例
javascript
17Knight20 分钟前
我的个性化 VSCode
前端
前端小菜鸟一枚s23 分钟前
`ConstantProperty` 的使用与应用
前端·javascript·cesium
狠狠的学习29 分钟前
antd表格行hover效果性能处理
前端·css
在下小航33 分钟前
前端本地大模型 window.ai 最新教程
前端·人工智能