我的编程空间,编程开发者的网络收藏夹
学习永远不晚

js中区分深拷贝与浅拷贝的实战过程

短信预约 -IT技能 免费直播动态提醒
省份

北京

  • 北京
  • 上海
  • 天津
  • 重庆
  • 河北
  • 山东
  • 辽宁
  • 黑龙江
  • 吉林
  • 甘肃
  • 青海
  • 河南
  • 江苏
  • 湖北
  • 湖南
  • 江西
  • 浙江
  • 广东
  • 云南
  • 福建
  • 海南
  • 山西
  • 四川
  • 陕西
  • 贵州
  • 安徽
  • 广西
  • 内蒙
  • 西藏
  • 新疆
  • 宁夏
  • 兵团
手机号立即预约

请填写图片验证码后获取短信验证码

看不清楚,换张图片

免费获取短信验证码

js中区分深拷贝与浅拷贝的实战过程

一、自我理解

简单来讲就是:深拷贝层层拷贝,浅拷贝只拷贝第一层。

在深拷贝中,新对象中的更改不会影响原对象,而在浅拷贝中,新对象中的更改,原对象中也会跟着改。

在深拷贝中,原对象与新对象不共享相同的属性,而在浅拷贝中,它们具有相同的属性。

举个栗子:存在A和B两个数据,假设B复制了A,当修改A时,如果B跟着A变化了,则是浅拷贝;如果B不受A的改变而改变,则是深拷贝

let A = [0,1,2]
let B = A
console.log(A === B);
A[0] = 3
console.log(A,B);

结果如下:

这就是一个简单的浅拷贝例子,虽然B复制了A,但是修改了A,B却跟着A变化了?

二、数据存储形式

通过上述栗子,我们就需要了解数据类型的存储方式了,如果还没有了解数据类型的小伙伴欢迎阅读 JS中8种数据类型

  • 基本数据类型:number,string,boolean,null,undefined,symbol(es6),还有谷歌67版本中的BigInt(任意精度整数)
  • 引用数据类型:Object【Object是个大类,function函数、array数组、date日期...等都归属于Object】

(1)基本数据类型存储于栈中

变量名和值都存储与栈中

每当声明一个变量,就会开辟一个新的内存空间来存储值,例如:let a = 1

// 声明一个变量
let a = 1;

当b复制了a后,因为b也是一个变量,所以栈内存会再开辟一个新的内存空间:

// 声明一个变量
let a = 1;
let b = a; //b变量复制a变量
console.log(a,b); //1 1

这就一目了然了,a和b两个变量归属于不同的内存空间,所以当你修改其中一个值,另外一个值不会受其影响!

// 声明一个变量
let a = 1;
let b = a; //b变量复制a变量
console.log(a,b); //1 1 
// 修改a的值不影响b
a = 123;
console.log(a,b); //123 1

(2)引用数据类型存储与堆中

变量名存储在栈中,值存在于堆中,这时栈内存中会提供一个指针用于指向堆内存中的值

当我们声明一个引用数据类型时,会提供一个指针指向堆内存中的值:

// 声明一个引用类型的值
let a = [0,1,2,3,4]

当b复制了a后,只是复制了当前栈内存中提供的指针,指向同一个堆内存中的值,并不是复制了堆内存中的值:

let a = [0,1,2,3,4]
let b = a //复制的是一个指针,而不是堆中的值

所以当我们进行a[0]=1时,导致指针所指向的堆内存中的值发生了改变,而a和b是同一个指针,指向同一个堆内存地址,所以b的值也会受影响,这就是浅拷贝!

// 声明一个引用类型的值
let a = [0,1,2,3,4];
let b = a; //复制的是一个指针,而不是堆中的值
console.log(a === b);
a[0] = 1; //改变a数组值,b数组跟着改变(浅拷贝)
console.log(a,b);

所以,我们需要在堆内存中新开辟一个内存专门接收存储b的值,道理与基本数据类型一样,这样就可以实现深拷贝(修改一个值不会影响另一个值的变化):

三、怎样实现深拷贝?

(1)借助JSON对象的parse和stringify

parse()方法用于将一个字符串解析为json对象

stringify()方法用于将一个对象解析为字符串

// 浅拷贝
let arr = [1,"hello",{name:"张三",age:21}]
let copyArr = arr
console.log(arr === copyArr); //true
arr[1] = "您好"
console.log("浅拷贝",arr, copyArr);

// JSON对象方法实现深拷贝
let arr = [1,"hello",{name:"张三",age:21}]
let newArr = JSON.parse(JSON.stringify(arr)) //将arr数组转换为字符串,再作为参数转化为对象
arr[1] = "您好"
arr[2].name = "法外狂徒"
console.log("深拷贝",arr, newArr);

原理是利用JSON.stringify将对象转成JSON字符串,再用JSON.parse把字符串解析成对象,这样在堆中开辟了新的内存,实现深拷贝

这种写法非常简单,而且可以应对大部分的应用场景,但是它还是有很大缺陷的,比如拷贝其他引用类型、拷贝函数、循环引用等情况

(2)手写递归

递归方法实现深度克隆原理:遍历对象、数组...直到里边都是基本数据类型,然后再去复制,就是深度拷贝

  • 如果是原始类型,无需继续拷贝,直接返回
  • 如果是引用类型,创建一个新的对象,遍历需要克隆的对象,将需要克隆对象的属性执行深拷贝后依次添加到新对象上
// 手写递归实现深拷贝
function clone(target) {
    if (typeof target === 'object') {
        // 创建一个新对象
        let cloneTarget = {};
        // 遍历需要克隆的对象
        for (const key in target) {
            // 将需要克隆对象的属性执行深拷贝后依次添加到新对象上
            cloneTarget[key] = clone(target[key]);
        }
        // 返回克隆对象
        return cloneTarget;
    } else {
        return target;
    }
};
// 模拟拷贝对象
const person = {
    name: "李小白",
    age: 18,
    job: {
        name:"web前端",
        salary:"15k",
        address:"成都"
    }
};
// 将需要克隆的对象传递给clone函数作为参数,接收一个克隆对象作为返回结果
let newTarget = clone(person)
//修改原对象属性
person["name"] = "李大白" 
person["job"]["salary"] = "25k" 
console.log(person,newTarget); //实现深拷贝

利用递归可以实现深度拷贝,但是有局限性,例如数组还没考虑进去!

想要兼容数组其实很简单,直接判断一下参数是不是数组,然后利用三木运算决定是创建一个新对象还是一个新数组:

function clone(target) {
    if (typeof target === 'object') {
        let isArr = Array.isArray(target)
        // 判断传入的参数是数组吗 ? 是则赋值[] : 否则赋值{}
        let cloneTarget = isArr ? [] : {};
        // 遍历需要克隆的对象
        for (const key in target) {
            // 将需要克隆对象的属性执行深拷贝后依次添加到新对象上
            cloneTarget[key] = clone(target[key]);
        }
        // 返回克隆对象
        return cloneTarget;
    } else {
        return target;
    }
};

(3)JQuery中extend方法

在jQuery中提供一个$extend()来实现深拷贝

语法:

$.extend( [deep ], target, object1 [, objectN ] )
  • deep 表示是否深拷贝,为true为深拷贝,为false,则为浅拷贝
  • target Object类型 目标对象,其他对象的成员属性将被附加到该对象上。
  • object1  objectN可选。 Object类型 第一个以及第N个被合并的对象。
let arr = ["李小白",18,["前端","15k"],21]
// true:开启深拷贝,目标对象:[],原对象:arr
let cloneArr = $.extend(true, [], arr)
arr[0] = "李大白";
arr[2][0] = "java";
console.log(arr, cloneArr);

$extend()第一个参数为true时可以实现深拷贝!但是第一个参数为false,则只会拷贝第一层属性,不能实现深拷贝:

let arr = ["李小白",18,["前端","15k"],21]
// false:不开启深拷贝,目标对象:[],原对象:arr
let cloneArr = $.extend(false, [], arr)
arr[0] = "李大白";
arr[2][0] = "java";
console.log(arr, cloneArr);

总结/注意

实现深拷贝方式有多种,以上三种较为常用,另外还有一个:函数库lodash中_.cloneDeep方法(本人未使用过)也可以实现深拷贝。

最后需要补充一下,网上有很多人说slice和concat方法可以实现,但我要说的是slice和concat方法不能实现深拷贝的!!!

下面举例说明:

// slice()
let arr = [0,1,["hello","world"]];
let cloneArr = arr.slice();
arr[0] = "零";
arr[2][0] = "hello2022"
console.log(arr,cloneArr);
// concat()
let arr = [0,1,["hello","world"]];
let cloneArr = arr.concat();
arr[0] = "零";
arr[2][0] = "hello2022"
console.log(arr,cloneArr);

这两个方法得到的结果都是一样的:

这一点大家一定要注意,深拷贝必须要拷贝所有层级的属性,而这两个方法拷贝不彻底,也就是只能拷贝第一层,希望大家不要踩坑!

总结

到此这篇关于js中区分深拷贝与浅拷贝的文章就介绍到这了,更多相关js区分深拷贝与浅拷贝内容请搜索编程网以前的文章或继续浏览下面的相关文章希望大家以后多多支持编程网!

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

js中区分深拷贝与浅拷贝的实战过程

下载Word文档到电脑,方便收藏和打印~

下载Word文档

猜你喜欢

java中的浅拷贝与深拷贝

1、什么叫Java浅拷贝?浅拷贝是会将对象的每个属性进行依次复制,但是当对象的属性值是引用类型时,实质复制的是其引用,当引用指向的值改变时也会跟着变化。2、什么叫Java深拷贝?深拷贝复制变量值,对于引用数据,则递归至基本类型后,再复制。深拷贝后的对象与原来的
java中的浅拷贝与深拷贝
2021-10-17

js中深拷贝和浅拷贝的区别有哪些

这篇文章主要介绍js中深拷贝和浅拷贝的区别有哪些,文中介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们一定要看完!javascript是一种什么语言javascript是一种动态类型、弱类型的语言,基于对象和事件驱动并具有相对安全性并广
2023-06-14

通过源码分析iOS中的深拷贝与浅拷贝

前言 关于iOS中对象的深拷贝和浅拷贝的文章有很多,但是大部分都是基于打印内存地址来推导结果,这篇文章是从源码的角度来分析深拷贝和浅拷贝。 深拷贝和浅拷贝的概念拷贝的方式有两种:深拷贝和浅拷贝。浅拷贝又叫指针拷贝,比如说有一个指针,这个指针
2022-06-04

c++中深拷贝和浅拷贝的区别

深度拷贝和浅拷贝是 c++ 中复制对象的方法,它们在复制对象的行为上有着本质的区别:浅拷贝仅复制对象的指针或引用,原始对象的数据可能会受到影响。深拷贝复制对象的所有数据,创建新的对象,不会影响原始对象的数据。C++ 中的深拷贝与浅拷贝深拷
c++中深拷贝和浅拷贝的区别
2024-05-12

Python中浅拷贝和深拷贝的区别总结与

单层浅拷贝import copya = 1 # 不可变数据类型copy_a = copy.copy(a)print(id(a),id(copy_a)) # 内存地址相同a = [1,2] # 可变数据类型copy_a = copy.c
2023-01-30

JavaScript中深拷贝与浅拷贝实例运用

这篇文章主要介绍了JavaScript中深拷贝与浅拷贝实例运用的相关知识,内容详细易懂,操作简单快捷,具有一定借鉴价值,相信大家阅读完这篇JavaScript中深拷贝与浅拷贝实例运用文章都会有所收获,下面我们一起来看看吧。1 浅拷贝概念深拷
2023-06-29

C++中深拷贝与浅拷贝有什么区别

这篇文章将为大家详细讲解有关C++中深拷贝与浅拷贝有什么区别,文章内容质量较高,因此小编分享给大家做个参考,希望大家阅读完这篇文章后对相关知识有一定的了解。浅拷贝只是对指针的拷贝,拷贝后两个指针指向同一个内存空间;深拷贝对指针指向的内容进行
2023-06-14

Golang中的深拷贝与浅拷贝使用

本文主要介绍了Golang中的深拷贝与浅拷贝使用,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
2023-05-14

JavaScript中的浅拷贝和深拷贝原理与实现浅析

这篇文章主要介绍了JavaScript中的浅拷贝和深拷贝原理与实现,JavaScript中的浅拷贝和深拷贝指的是在复制对象(包括对象、数组等)时,是否只复制对象的引用地址或者在复制时创建一个新的对象
2023-05-17

编程热搜

目录