新用户注册 | 会员登录
登录 极速注册
取消
热门标签 | HotTags
当前位置:   开发笔记 > 编程语言 > 正文

怎么理解Javascript深拷贝与浅拷贝

怎么理解Javascript深拷贝与浅拷贝,针对这个问题,这篇文章详细介绍了相对应的分析和解答,希望可以帮助更多想解决这个问题的小伙伴找到更简单易行的

怎么理解Javascript深拷贝与浅拷贝,针对这个问题,这篇文章详细介绍了相对应的分析和解答,希望可以帮助更多想解决这个问题的小伙伴找到更简单易行的方法。

 前言

在 Javascript  中有不同的方法来复制对象,如果你还不熟悉这门语言的话,复制对象时就会很容易掉进陷阱里,那么我们怎样才能正确地复制一个对象呢?

浅拷贝与深拷贝

  • 浅拷贝是创建一个新对象,这个对象有着原始对象属性值的一份精确拷贝。如果属性是基本类型,拷贝的就是基本类型的值,如果属性是引用类型,拷贝的就是内存地址  ,所以如果其中一个对象改变了这个地址,就会影响到另一个对象。

  • 深拷贝是将一个对象从内存中完整的拷贝一份出来,从堆内存中开辟一个新的区域存放新对象,且修改新对象不会影响原对象。

var a1 = {b: {c: {}};  var a2 = shallowClone(a1); // 浅拷贝方法 a2.b.c === a1.b.c // true 新旧对象还是共享同一块内存  var a3 = deepClone(a3); // 深拷贝方法 a3.b.c === a1.b.c // false 新对象跟原对象不共享内存

借助ConardLi大佬以下两张图片,帮我们更好的理解两者的含义:

怎么理解Javascript深拷贝与浅拷贝

怎么理解Javascript深拷贝与浅拷贝

总而言之,浅拷贝只复制指向某个对象的指针,而不复制对象本身,新旧对象还是共享同一块内存。但深拷贝会另外创造一个一模一样的对象,新对象跟原对象不共享内存,修改新对象不会改到原对象。

赋值和深/浅拷贝的区别

这三者的区别如下,不过比较的前提都是针对引用类型:

  • 当我们把一个对象赋值给一个新的变量时,赋的其实是该对象的在栈中的地址,而不是堆中的数据。也就是两个对象指向的是同一个存储空间,无论哪个对象发生改变,其实都是改变的存储空间的内容,因此,两个对象是联动的。

  • 浅拷贝:重新在堆中创建内存,拷贝前后对象的基本数据类型互不影响,但拷贝前后对象的引用类型因共享同一块内存,会相互影响。

  • 深拷贝:从堆内存中开辟一个新的区域存放新对象,对对象中的子对象进行递归拷贝,拷贝前后的两个对象互不影响。

我们先来看下面的例子,对比赋值与深/浅拷贝得到的对象修改后对原始对象的影响:

// 对象赋值 let obj1 = {     name : '浪里行舟',     arr : [1,[2,3],4], }; let obj2 = obj1; obj2.name = "阿浪"; obj2.arr[1] =[5,6,7] ; console.log('obj1',obj1)  // obj1 { name: '阿浪', arr: [ 1, [ 5, 6, 7 ], 4 ] } console.log('obj2',obj2)  // obj2 { name: '阿浪', arr: [ 1, [ 5, 6, 7 ], 4 ] }
// 浅拷贝 let obj1 = {     name : '浪里行舟',     arr : [1,[2,3],4], }; let obj3=shallowClone(obj1) obj3.name = "阿浪"; obj3.arr[1] = [5,6,7] ; // 新旧对象还是共享同一块内存 // 这是个浅拷贝的方法 function shallowClone(source) {     var target = {};     for(var i in source) {         if (source.hasOwnProperty(i)) {             target[i] = source[i];         }     }     return target; } console.log('obj1',obj1)  // obj1 { name: '浪里行舟', arr: [ 1, [ 5, 6, 7 ], 4 ] } console.log('obj3',obj3)  // obj3 { name: '阿浪', arr: [ 1, [ 5, 6, 7 ], 4 ] }
// 深拷贝 let obj1 = {     name : '浪里行舟',     arr : [1,[2,3],4], }; let obj4=deepClone(obj1) obj4.name = "阿浪"; obj4.arr[1] = [5,6,7] ; // 新对象跟原对象不共享内存 // 这是个深拷贝的方法 function deepClone(obj) {     if (obj === null) return obj;      if (obj instanceof Date) return new Date(obj);     if (obj instanceof RegExp) return new RegExp(obj);     if (typeof obj !== "object") return obj;     let cloneObj = new obj.constructor();     for (let key in obj) {       if (obj.hasOwnProperty(key)) {         // 实现一个递归拷贝         cloneObj[key] = deepClone(obj[key]);       }     }     return cloneObj; } console.log('obj1',obj1)  // obj1 { name: '浪里行舟', arr: [ 1, [ 2, 3 ], 4 ] } console.log('obj4',obj4)  // obj4 { name: '阿浪', arr: [ 1, [ 5, 6, 7 ], 4 ] }

上面例子中,obj1是原始对象,obj2是赋值操作得到的对象,obj3浅拷贝得到的对象,obj4深拷贝得到的对象,通过下面的表格,我们可以很清晰看到他们对原始数据的影响:

怎么理解Javascript深拷贝与浅拷贝

浅拷贝的实现方式

1.Object.assign()

Object.assign() 方法可以把任意多个的源对象自身的可枚举属性拷贝给目标对象,然后返回目标对象。

let obj1 = { person: {name: "kobe", age: 41},sports:'basketball' }; let obj2 = Object.assign({}, obj1); obj2.person.name = "wade"; obj2.sports = 'football' console.log(obj1);  // { person: { name: 'wade', age: 41 }, sports: 'basketball' }

2.函数库lodash的_.clone方法

该函数库也有提供_.clone用来做 Shallow Copy,后面我们会再介绍利用这个库实现深拷贝。

var _ = require('lodash'); var obj1 = {     a: 1,     b: { f: { g: 1 } },     c: [1, 2, 3] }; var obj2 = _.clone(obj1); console.log(obj1.b.f === obj2.b.f); // true

3.展开运算符...

展开运算符是一个 es6 / es2015特性,它提供了一种非常方便的方式来执行浅拷贝,这与 Object.assign ()的功能相同。

let obj1 = { name: 'Kobe', address:{x:100,y:100}} let obj2= {... obj1} obj1.address.x = 200; obj1.name = 'wade' console.log('obj2',obj2)  // obj2 { name: 'Kobe', address: { x: 200, y: 100 } }

4.Array.prototype.concat()

let arr = [1, 3, {     username: 'kobe'     }]; let arr2 = arr.concat();     arr2[2].username = 'wade'; console.log(arr);  //[ 1, 3, { username: 'wade' } ]

5.Array.prototype.slice()

let arr = [1, 3, {     username: ' kobe'     }]; let arr3 = arr.slice(); arr3[2].username = 'wade' console.log(arr);  // [ 1, 3, { username: 'wade' } ]

深拷贝的实现方式

1.JSON.parse(JSON.stringify())

let arr = [1, 3, {     username: ' kobe' }]; let arr4 = JSON.parse(JSON.stringify(arr)); arr4[2].username = 'duncan';  console.log(arr, arr4)

怎么理解Javascript深拷贝与浅拷贝

这也是利用JSON.stringify将对象转成JSON字符串,再用JSON.parse把字符串解析成对象,一去一来,新的对象产生了,而且对象会开辟新的栈,实现深拷贝。

这种方法虽然可以实现数组或对象深拷贝,但不能处理函数和正则,因为这两者基于JSON.stringify和JSON.parse处理后,得到的正则就不再是正则(变为空对象),得到的函数就不再是函数(变为null)了。

比如下面的例子:

let arr = [1, 3, {     username: ' kobe' },function(){}]; let arr4 = JSON.parse(JSON.stringify(arr)); arr4[2].username = 'duncan';  console.log(arr, arr4)

怎么理解Javascript深拷贝与浅拷贝

2.函数库lodash的_.cloneDeep方法

该函数库也有提供_.cloneDeep用来做 Deep Copy

var _ = require('lodash'); var obj1 = {     a: 1,     b: { f: { g: 1 } },     c: [1, 2, 3] }; var obj2 = _.cloneDeep(obj1); console.log(obj1.b.f === obj2.b.f); // false

3.jQuery.extend()方法

jquery 有提供一個$.extend可以用来做 Deep Copy

$.extend(deepCopy, target, object1, [objectN])//第一个参数为true,就是深拷贝
var $ = require('jquery'); var obj1 = {     a: 1,     b: { f: { g: 1 } },     c: [1, 2, 3] }; var obj2 = $.extend(true, {}, obj1); console.log(obj1.b.f === obj2.b.f);  // false

4.手写递归方法

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

有种特殊情况需注意就是对象存在循环引用的情况,即对象的属性直接的引用了自身的情况,解决循环引用问题,我们可以额外开辟一个存储空间,来存储当前对象和拷贝对象的对应关系,当需要拷贝当前对象时,先去存储空间中找,有没有拷贝过这个对象,如果有的话直接返回,如果没有的话继续拷贝,这样就巧妙化解的循环引用的问题。关于这块如有疑惑,请仔细阅读ConardLi大佬如何写出一个惊艳面试官的深拷贝?这篇文章。

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; } let obj = { name: 1, address: { x: 100 } }; obj.o = obj; // 对象存在循环引用的情况 let d = deepClone(obj); obj.address.x = 200; console.log(d);

关于怎么理解Javascript深拷贝与浅拷贝问题的解答就分享到这里了,希望以上内容可以对大家有一定的帮助,如果你还有很多疑惑没有解开,可以关注编程笔记行业资讯频道了解更多相关知识。


  • javascript
  • java
  • ip
  • clone
  • 图片
  • console
  • function
  • get
  • instance
推荐阅读
  • php

    前端人员必须知道的三个问题及其发展阶段

    本文介绍了前端人员必须知道的三个问题,即前端都做哪些事、前端都需要哪些技术,以及前端的发展阶段。初级阶段包括HTML、CSS、JavaScript和jQuery的基础知识。进阶阶段涵盖了面向对象编程、响应式设计、Ajax、HTML5等新兴技术。高级阶段包括架构基础、模块化开发、预编译和前沿规范等内容。此外,还介绍了一些后端服务,如Node.js。 ... [详细]
  • function

    绕过WAF的XSS检测机制及构建XSS payload的方法

    本文介绍了绕过WAF的XSS检测机制的方法,包括确定payload结构、测试和混淆。同时提出了一种构建XSS payload的方法,该payload与安全机制使用的正则表达式不匹配。通过清理用户输入、转义输出、使用文档对象模型(DOM)接收器和源、实施适当的跨域资源共享(CORS)策略和其他安全策略,可以有效阻止XSS漏洞。但是,WAF或自定义过滤器仍然被广泛使用来增加安全性。本文的方法可以绕过这种安全机制,构建与正则表达式不匹配的XSS payload。 ... [详细]
  • import

    利用RxJava实现的事件总线(Event Bus)及其使用方法介绍

    本文介绍了RxJava在Android开发中的广泛应用以及其在事件总线(Event Bus)实现中的使用方法。RxJava是一种基于观察者模式的异步java库,可以提高开发效率、降低维护成本。通过RxJava,开发者可以实现事件的异步处理和链式操作。对于已经具备RxJava基础的开发者来说,本文将详细介绍如何利用RxJava实现事件总线,并提供了使用建议。 ... [详细]
  • settings

    使用eclipse创建一个Java项目的步骤

    使用eclipse创建一个Java项目的步骤
    本文介绍了使用eclipse创建一个Java项目的步骤,包括启动eclipse、选择New Project命令、在对话框中输入项目名称等。同时还介绍了Java Settings对话框中的一些选项,以及如何修改Java程序的输出目录。 ... [详细]
  • function

    JS进修笔记——闭包的运转机制和作用域

    本文介绍了闭包的定义和运转机制,重点解释了闭包如何能够接触外部函数的作用域中的变量。通过词法作用域的查找规则,闭包可以访问外部函数的作用域。同时还提到了闭包的作用和影响。 ... [详细]
  • get

    【机器学习】生成式对抗网络模型综述

    生成式对抗网络模型综述摘要生成式对抗网络模型(GAN)是基于深度学习的一种强大的生成模型,可以应用于计算机视觉、自然语言处理、半监督学习等重要领域。生成式对抗网络 ... [详细]
  • import

    Python实现变声器功能(萝莉音御姐音)的方法及步骤

    Python实现变声器功能(萝莉音御姐音)的方法及步骤
    本文介绍了使用Python实现变声器功能(萝莉音御姐音)的方法及步骤。首先登录百度AL开发平台,选择语音合成,创建应用并填写应用信息,获取Appid、API Key和Secret Key。然后安装pythonsdk,可以通过pip install baidu-aip或python setup.py install进行安装。最后,书写代码实现变声器功能,使用AipSpeech库进行语音合成,可以设置音量等参数。 ... [详细]
  • php

    C#学习教程:在Console中工作但在Windows窗体中不工作的异步代码分享

    本文分享了一个关于在C#中使用异步代码的问题,作者在控制台中运行时代码正常工作,但在Windows窗体中却无法正常工作。作者尝试搜索局域网上的主机,但在窗体中计数器没有减少。文章提供了相关的代码和解决思路。 ... [详细]
  • php

    Metasploit攻击渗透实践

    Metasploit攻击渗透实践
    本文介绍了Metasploit攻击渗透实践的内容和要求,包括主动攻击、针对浏览器和客户端的攻击,以及成功应用辅助模块的实践过程。其中涉及使用Hydra在不知道密码的情况下攻击metsploit2靶机获取密码,以及攻击浏览器中的tomcat服务的具体步骤。同时还讲解了爆破密码的方法和设置攻击目标主机的相关参数。 ... [详细]
  • php

    C# 7.0 新特性:基于Tuple的“多”返回值方法

    C# 7.0 新特性:基于Tuple的“多”返回值方法
    本文介绍了C# 7.0中基于Tuple的“多”返回值方法的使用。通过对C# 6.0及更早版本的做法进行回顾,提出了问题:如何使一个方法可返回多个返回值。然后详细介绍了C# 7.0中使用Tuple的写法,并给出了示例代码。最后,总结了该新特性的优点。 ... [详细]
  • uri

    开发笔记:解决播放框架内容安全策略设置不起作用的问题

    本文介绍了作者在开发过程中遇到的问题,即播放框架内容安全策略设置不起作用的错误。作者通过使用编译时依赖注入的方式解决了这个问题,并分享了解决方案。文章详细描述了问题的出现情况、错误输出内容以及解决方案的具体步骤。如果你也遇到了类似的问题,本文可能对你有一定的参考价值。 ... [详细]
  • get

    Python爬虫技术基础篇面向对象高级编程(中)的多重继承

    本文介绍了Python爬虫技术基础篇面向对象高级编程(中)中的多重继承概念。通过继承,子类可以扩展父类的功能。文章以动物类层次的设计为例,讨论了按照不同分类方式设计类层次的复杂性和多重继承的优势。最后给出了哺乳动物和鸟类的设计示例,以及能跑、能飞、宠物类和非宠物类的增加对类数量的影响。 ... [详细]
  • function

    元素 - Dynamically create
  • element on click">单击时动态创建
  • 元素 - Dynamically create
  • element on click
  • Ihavethefollowingonhtml我在html上有以下内容<html><head><scriptsrc..3003_Tes ... 元素 - Dynamically create
  • element on click">[详细]
  • function

    wepy小顺序受权点击作废受权失利的计划

    本文介绍了在wepy中运用小顺序页面受权的计划,包含了用户点击作废后的从新受权计划。 ... [详细]
  • php

    微软的STL容器类实现是否线程安全?

    本文讨论了微软的STL容器类是否线程安全。根据MSDN的回答,STL容器类包括vector、deque、list、queue、stack、priority_queue、valarray、map、hash_map、multimap、hash_multimap、set、hash_set、multiset、hash_multiset、basic_string和bitset。对于单个对象来说,多个线程同时读取是安全的。但如果一个线程正在写入一个对象,那么所有的读写操作都需要进行同步。 ... [详细]
  • author-avatar
    W布二
    这个家伙很懒,什么也没留下!
    Tags | 热门标签
    • version
    • random
    • golang
    • get
    • erlang
    • filter
    • flutter
    • 数组
    • tags
    • uri
    • subset
    • join
    • settings
    • cPlusPlus
    • client
    • case
    • include
    • yaml
    • bitmap
    • merge
    • hook
    • md5
    • bytecode
    • loops
    • import
    • search
    • replace
    • web3
    • php
    • function
    RankList | 热门文章
    • 1 计算机显示器采用的颜色模型是什么
    • 2 笔记本电脑需要一直插着电源吗
    • 3 什么是.o文件,可以在Windows上打开它们?
    • 4 Lambda链接-使用Terraform从另一个Lambda调用Lambda
    • 5 与Phoenix和VSCode一起使用时,ElixirLS调试器意外停止
    • 6 从通用函数中删除一种方法
    • 7 BrowserStack-IE11上的错误SendKeys
    • 8 在R中使用str_extract在带有正则表达式的子字符串之前提取数字
    • 9 Sublime Text3 Github最受欢迎主题字体推荐!
    • 10 GIT 如何回退到某次 commit ?
    • 11 在Sublime Text3下如何使用MarkDown编辑器
    • 12 解决sublime中文光标跟随问题
    • 13 webstorm怎么调整字体大小
    • 14 vscode vsstudio区别
    • 15 vscode打开脚本出现中文乱码
    PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 | PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
    Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有
         

    深圳SEO优化公司同乐网站优化排名西乡百度关键词包年推广爱联企业网站改版惠州网站设计模板坪山网站优化按天扣费南山网站seo优化永湖百度seo布吉百姓网标王大芬品牌网站设计宝安网站制作坂田品牌网站设计盐田网站优化推广坪山网站推广工具坪地网站优化按天扣费东莞SEO按天扣费深圳外贸网站设计观澜百度标王木棉湾外贸网站制作大鹏SEO按天收费南山seo网站推广龙华百搜词包坪山网站设计模板坂田seo排名松岗网站优化软件广州高端网站设计大芬网站建设大芬网站推广方案大运网站推广方案坂田网页设计光明SEO按天计费歼20紧急升空逼退外机英媒称团队夜以继日筹划王妃复出草木蔓发 春山在望成都发生巨响 当地回应60岁老人炒菠菜未焯水致肾病恶化男子涉嫌走私被判11年却一天牢没坐劳斯莱斯右转逼停直行车网传落水者说“没让你救”系谣言广东通报13岁男孩性侵女童不予立案贵州小伙回应在美国卖三蹦子火了淀粉肠小王子日销售额涨超10倍有个姐真把千机伞做出来了近3万元金手镯仅含足金十克呼北高速交通事故已致14人死亡杨洋拄拐现身医院国产伟哥去年销售近13亿男子给前妻转账 现任妻子起诉要回新基金只募集到26元还是员工自购男孩疑遭霸凌 家长讨说法被踢出群充个话费竟沦为间接洗钱工具新的一天从800个哈欠开始单亲妈妈陷入热恋 14岁儿子报警#春分立蛋大挑战#中国投资客涌入日本东京买房两大学生合买彩票中奖一人不认账新加坡主帅:唯一目标击败中国队月嫂回应掌掴婴儿是在赶虫子19岁小伙救下5人后溺亡 多方发声清明节放假3天调休1天张家界的山上“长”满了韩国人?开封王婆为何火了主播靠辱骂母亲走红被批捕封号代拍被何赛飞拿着魔杖追着打阿根廷将发行1万与2万面值的纸币库克现身上海为江西彩礼“减负”的“试婚人”因自嘲式简历走红的教授更新简介殡仪馆花卉高于市场价3倍还重复用网友称在豆瓣酱里吃出老鼠头315晚会后胖东来又人满为患了网友建议重庆地铁不准乘客携带菜筐特朗普谈“凯特王妃P图照”罗斯否认插足凯特王妃婚姻青海通报栏杆断裂小学生跌落住进ICU恒大被罚41.75亿到底怎么缴湖南一县政协主席疑涉刑案被控制茶百道就改标签日期致歉王树国3次鞠躬告别西交大师生张立群任西安交通大学校长杨倩无缘巴黎奥运

    深圳SEO优化公司 XML地图 TXT地图 虚拟主机 SEO 网站制作 网站优化