JavaScript的深浅复制
JavaScript的深浅复制
为什么有深复制、浅复制?
JavaScript中有两种数据类型,基本数据类型如undefined、null、boolean、number、string,另一类是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 ]
参考连接
- 摸索 JS 内深拷贝的最佳实践 - 简单易懂的前端角 - SegmentFault 思否
- 理解JavaScript:不可变的原始值与可变的对象引用
- [ JS 进阶 ] 基本类型 引用类型 简单赋值 对象引用 - kraaas前端博客 - SegmentFault 思否
- 什么是js深拷贝和浅拷贝及其实现方式
- js 深拷贝 vs 浅拷贝 - 掘金
- 深拷贝的终极探索(99%的人都不知道) - 颜海镜 - SegmentFault 思否
JavaScript的深浅复制的更多相关文章
- 详谈OC(object-c)深浅复制/拷贝-什么情况下用retain和copy
读前小提示:对于深浅复制有一个清楚的了解,对于学习oc的朋友来说,至关重要.那么首先,我们要明白深浅复制是如何定义的呢.这里为了便于朋友们理解,定义如下. 浅 复 制:在复制操作时,对于被复制的对象的 ...
- OC-深浅复制
[OC学习-26]对象的浅拷贝和深拷贝——关键在于属性是否可被拷贝 对象的拷贝分为浅拷贝和深拷贝, 浅拷贝就是只拷贝对象,但是属性不拷贝,拷贝出来的对象和原来的对象共用属性,即指向同一个属性地址. 深 ...
- Objective-c 深浅复制
深浅复制的定义: 浅复制:在复制时,对于被复制对象的每一层都是指针复制. 深复制:在复制时,对于被复制的对象至少有一层是对象复制. 完全复制:在复制时,对于被复制对象的每一层都是完全复制. retai ...
- Python基础之列表深浅复制和列表推导式
一.列表深浅复制: 浅拷贝内存图如下: 深拷贝内存图如下: 二.列表推导式: 实例: """ 列表推导式 练习:exercise01 """ ...
- C# 深浅复制 MemberwiseClone(转载)
最近拜读了大话设计模式:原型模式,该模式主要应用C# 深浅复制来实现的!关于深浅复制大家可参考MSDN:https://docs.microsoft.com/zh-cn/dotnet/api/syst ...
- C# 深浅复制 MemberwiseClone
学无止境,精益求精 十年河东,十年河西,莫欺少年穷 学历代表你的过去,能力代表你的现在,学习代表你的将来 最近拜读了大话设计模式:原型模式,该模式主要应用C# 深浅复制来实现的!关于深浅复制大家可参考 ...
- 潭州课堂25班:Ph201805201 第五课:格式化输出和深浅复制 (课堂笔记)
格式化输出和字符串转义 占位符 使用示意 作用 %s '%s %s' % ('hello', 'world') 表示占位的是str %d '%d %d' % (1, 2) 表示占位的是int %d ' ...
- C#设计模式(6)——原型模式(Prototype Pattern) C# 深浅复制 MemberwiseClone
C#设计模式(6)——原型模式(Prototype Pattern) 一.引言 在软件系统中,当创建一个类的实例的过程很昂贵或很复杂,并且我们需要创建多个这样类的实例时,如果我们用new操作符去创 ...
- 【Python初级】由判定回文数想到的,关于深浅复制,以及字符串反转的问题
尝试用Python实现可以说是一个很经典的问题,判断回文数. 让我们再来看看回文数是怎么定义的: 回数是指从左向右读和从右向左读都是一样的数,例如1,121,909,666等 解决这个问题的思路,可以 ...
随机推荐
- ActionFilter、IAuthorizationFilter 权限验证重定向跳转到其它页面
方法一: public class IsAllowAttribute: ActionFilterAttribute { public override void OnActionExecuting(A ...
- vs2015编译OBS-Studio21.1.12
原文地址:http://www.freesion.com/article/37445100/ 参考:https://blog.csdn.net/su_vast/article/details/7498 ...
- JAVA:使用栈实现一个队列
使用栈实现一个队列,需要弄清楚栈和队列的区别: 栈:先进后出: 队列:先进先出. 实现思路: 1)通过两个栈(pushStack / popStack)对倒,确保 popStack 栈的出栈顺序与队列 ...
- Spring Boot-intellij idea导入方式搭建SpringBoot
Spring Boot概念 从最根本上来讲,Spring Boot就是一些库的集合,它能够被任意项目的构建系统所使用.简便起见,该框架也提供了命令行界面,它可以用来运行和测试Boot应用.框架的发布版 ...
- spring @Autowired注入map
注入map,平常一般不会这么做,今天看一段老代码时发现有这么个用法.补习一下. @Autowired 标注作用于 Map 类型时,如果 Map 的 key 为 String 类型,则 Spring 会 ...
- Spring cloud微服务安全实战-7-8ELK+SpringBoot环境搭建
采集不可聚合的离散的.日志信息的e ELK是三个系统的简称 LogStash:用来做日志的收集.过滤.格式转换 Kibana:和普罗米修斯的grafana一个意思.主要用来展示数据. 用docker来 ...
- LayaIDE 报typescript编译版本不一致的错
LayaIDE 报typescript编译版本不一致的错 文件 -> 首选项->用户设置那里修改下 settings.json // 将设置放入此文件中以覆盖默认设置 { "ty ...
- 使用ffmpeg -re循环推流(循环读取视频文件)推送EasyDSS RTMP、HLS(m3u8)、HTTP-FLV、RTSP流媒体服务器的方法
需求分析 众所周知,EasyDSS与EasyNVR最大的区别是,EasyDSS被动接受前端设备的推流,将推送过来的直播流进行直播转码.智能处理.视频分发,在通过CDN分发节点分发到终端播放SDK.而E ...
- net ads join 和net rpc join命令的区别
要将主机加入Active Directory(AD),请输入: #net ads加入-U administrator 输入管理员密码:Passw0rd 使用短域名 - SAMDOM 加入'M1'到dn ...
- precommit那些事儿
一.使用背景 我们有将 lint 命令添加进 npm scripts 中,但是很多人在提交代码时都会忘记或者没有习惯去执行检查,结果就是导致不符合规范的代码被上传到远端代码仓库. 二.问题分析 我们可 ...