JavaScript的深浅复制

为什么有深复制、浅复制?

JavaScript中有两种数据类型,基本数据类型如undefinednullbooleannumberstring,另一类是Object。简单数据类型只存储在内存中的栈区,复制的时候是值传递给新的索引。而复杂数据类型由栈区和堆区共同储存,栈区执行同样的操作,只是把堆地址复制了一份,而真实数据在堆区中依然只有一份。

为了不影响原有数据,那么我们就新建一个对象,遍历原有对象的属性赋值到新属性。

let newObj = {}
for (let prop in obj) {
newObj[prop] = obj[prop]
}

上面这个循环也可以用Object.assign({}, obj);来实现。

这样做是否解决问题?未必,因为Object中可以嵌套Object,如果原有对象属性中有复杂数据类型,那么新的对象中也只能得到一个地址。这种情况被称为浅复制。我们希望能将对象中的对象,无论多少层,都能复制一份,能达到这种效果的,称为深复制

深复制的几种方法

首先假设有数据

let obj = {
a: 23,
b: [0, 1, [2, 3], function() {console.log('in array')}, undefined],
c: {k: 'value'},
d: function() {console.log('a')}
}

JSON.parse(JSON.stringify(obj))

let newObj = JSON.parse(JSON.stringify(obj))
newObj.newKey = 'newValue'
console.log(obj)
console.log(newObj)

如果处理对象只是简单的键值对,这个方法效果不错。

这个方法的缺点

  • 无法复制函数
  • 忽略undefined
  • 无法处理Set、Map、Symbol类型(即使用上repalce参数)
  • 原有的原型链会消失
  • 循环引用的对象会报错

递归法

因为要处理属性的值也是Object这种情况,自然可以想到递归这种处理方法。

function deepCopy(oldObj, newObj) {
let obj = newObj || {}
for (let i in oldObj) {
if (oldObj[i] === obj) { // 防止循环引用
continue
} if (typeof oldObj[i] === 'object') {
// obj[i] = (oldObj[i].constructor === Array) ? [] : {}
obj[i] = oldObj[i] instanceof Array ? [] : {}
deepCopy(oldObj[i], obj[i])
} else {
obj[i] = oldObj[i]
}
}
return obj;
}

这样就能处理一个嵌套了Object和Array等复杂变量的对象。但是对象中还可能包含Date和RegExp对象、Set、Map……

Lodash库

const lodash = require('lodash')
let newObj = lodash.cloneDeep(obj)

数组的复制

let arr = [1, 2, 3, [4, 5], {a: 1}]

let copy = arr
copy.push(6) let copy1 = [...arr]
copy1.push(999) let copy2 = Array.from(arr)
copy2.push(888) let copy3 = arr.slice()
copy3.push(777) // 以上方法都是浅拷贝
arr[4].a = 2 console.log(arr) // [ 1, 2, 3, [ 4, 5 ], { a: 2 }, 6 ]
console.log(copy1) // [ 1, 2, 3, [ 4, 5 ], { a: 2 }, 6, 999 ]
console.log(copy2) // [ 1, 2, 3, [ 4, 5 ], { a: 2 }, 6, 888 ]
console.log(copy3) // [ 1, 2, 3, [ 4, 5 ], { a: 2 }, 6, 777 ]

参考连接

  1. 摸索 JS 内深拷贝的最佳实践 - 简单易懂的前端角 - SegmentFault 思否
  2. 理解JavaScript:不可变的原始值与可变的对象引用
  3. [ JS 进阶 ] 基本类型 引用类型 简单赋值 对象引用 - kraaas前端博客 - SegmentFault 思否
  4. 什么是js深拷贝和浅拷贝及其实现方式
  5. js 深拷贝 vs 浅拷贝 - 掘金
  6. 深拷贝的终极探索(99%的人都不知道) - 颜海镜 - SegmentFault 思否

JavaScript的深浅复制的更多相关文章

  1. 详谈OC(object-c)深浅复制/拷贝-什么情况下用retain和copy

    读前小提示:对于深浅复制有一个清楚的了解,对于学习oc的朋友来说,至关重要.那么首先,我们要明白深浅复制是如何定义的呢.这里为了便于朋友们理解,定义如下. 浅 复 制:在复制操作时,对于被复制的对象的 ...

  2. OC-深浅复制

    [OC学习-26]对象的浅拷贝和深拷贝——关键在于属性是否可被拷贝 对象的拷贝分为浅拷贝和深拷贝, 浅拷贝就是只拷贝对象,但是属性不拷贝,拷贝出来的对象和原来的对象共用属性,即指向同一个属性地址. 深 ...

  3. Objective-c 深浅复制

    深浅复制的定义: 浅复制:在复制时,对于被复制对象的每一层都是指针复制. 深复制:在复制时,对于被复制的对象至少有一层是对象复制. 完全复制:在复制时,对于被复制对象的每一层都是完全复制. retai ...

  4. Python基础之列表深浅复制和列表推导式

    一.列表深浅复制: 浅拷贝内存图如下: 深拷贝内存图如下: 二.列表推导式: 实例: """ 列表推导式 练习:exercise01 """ ...

  5. C# 深浅复制 MemberwiseClone(转载)

    最近拜读了大话设计模式:原型模式,该模式主要应用C# 深浅复制来实现的!关于深浅复制大家可参考MSDN:https://docs.microsoft.com/zh-cn/dotnet/api/syst ...

  6. C# 深浅复制 MemberwiseClone

    学无止境,精益求精 十年河东,十年河西,莫欺少年穷 学历代表你的过去,能力代表你的现在,学习代表你的将来 最近拜读了大话设计模式:原型模式,该模式主要应用C# 深浅复制来实现的!关于深浅复制大家可参考 ...

  7. 潭州课堂25班:Ph201805201 第五课:格式化输出和深浅复制 (课堂笔记)

    格式化输出和字符串转义 占位符 使用示意 作用 %s '%s %s' % ('hello', 'world') 表示占位的是str %d '%d %d' % (1, 2) 表示占位的是int %d ' ...

  8. C#设计模式(6)——原型模式(Prototype Pattern) C# 深浅复制 MemberwiseClone

    C#设计模式(6)——原型模式(Prototype Pattern)   一.引言 在软件系统中,当创建一个类的实例的过程很昂贵或很复杂,并且我们需要创建多个这样类的实例时,如果我们用new操作符去创 ...

  9. 【Python初级】由判定回文数想到的,关于深浅复制,以及字符串反转的问题

    尝试用Python实现可以说是一个很经典的问题,判断回文数. 让我们再来看看回文数是怎么定义的: 回数是指从左向右读和从右向左读都是一样的数,例如1,121,909,666等 解决这个问题的思路,可以 ...

随机推荐

  1. 【大数据应用技术】作业十|分布式文件系统HDFS 练习

    本次作业的要求来自:https://edu.cnblogs.com/campus/gzcc/GZCC-16SE2/homework/3292 1.目录操作 在HDFS中为hadoop用户创建一个用户目 ...

  2. 第09组 Beta冲刺(2/4)

    队名:软工9组 组长博客:https://www.cnblogs.com/cmlei/ 作业博客:https://edu.cnblogs.com/campus/fzu/SoftwareEngineer ...

  3. Spark2.x(五十九):yarn-cluster模式提交Spark任务,如何关闭client进程?

    问题: 最近现场反馈采用yarn-cluster方式提交spark application后,在提交节点机上依然会存在一个yarn的client进程不关闭,又由于spark application都是 ...

  4. SQLServer数据库之SQL Server 获取本周,本月,本年等时间内记录

    本文主要向大家介绍了SQLServer数据库之SQL Server 获取本周,本月,本年等时间内记录,通过具体的内容向大家展现,希望对大家学习SQLServer数据库有所帮助. datediff(we ...

  5. [转]白话HTTP短连接中的Session和Token

    我经常想象并怀念三十年前那原始而美好的互联网旧时光, 工作很轻松, 生活很悠闲. 上班的时候偶尔有些HTTP的请求发到我这里, 我简单的看一下, 取出相对应的html文档,图片,发回去就可以了, 然后 ...

  6. Spark程序进行单元测试-使用scala

    Spark 中进行一些单元测试技巧:最近刚写了一点Spark上的单元测试,大概整理了一些 rdd测试 spark程序一般从集群中读取数据然后通过rdd进行转换,这其中涉及到集群,每次修改bug,上传到 ...

  7. CefSharp 提示 flash player is out of date 运行此插件 等问题解决办法

    CefSharp 提示 flash player is out of date 或者 需要手动右键点 运行此插件 脚本 等问题解决办法 因为中国版FlashPlayer变得Ad模式之后,只好用旧版本的 ...

  8. django安装使用xadmin

    Xadmin介绍 直接替换掉Django自带的admin系统,并提供了很多有用的东西:完全的可扩展的插件支持,基于Twitter Bootstrap的漂亮UI. 完全替代Django admin 支持 ...

  9. Coder 健康 知识

  10. ScheduledExecutorService调度线程池运行几次后停止某一个线程

    开发中偶尔会碰到一些轮询需求,比如我碰到的和银行对接,在做完某一个业务后银行没有同步给到结果,这时候就需要查询返回结果,我们的需求是5分钟一次,查询3次,3次过后如果没有结果则T+1等银行的文件,对于 ...