前言
本文的浅拷贝、深拷贝
只有引用类型,基本类型没有这概念,
如果你还不知道什么是基本类型 / 引用类型
可以参考前面写过的 JS 什么是基本类型和引用类型,然后再回头看本文,或许对你有帮助,下面开始进入正文。
1. 什么是浅拷贝
拷贝的只有一层,多层会受影响(这个影响指的是修改数据时双方都会同步更新,无法真正的断开)。浅拷贝方式以下有几种
1、使用扩展符 [...]
代码如下
let colors = ['red', 'orange', 'blue', 'yellow', [{name: 'Jack'}]]
let copyColors = [...colors]
copyColors[0] = 'xxx'
console.log(colors)
console.log(copyColors)
// 可以看到第一层断开了
['red', 'orange', 'blue', 'yellow', [{name: 'Jack'}]]
['xxx', 'orange', 'blue', 'yellow', [{name: 'Jack'}]]
// 可以看到第二层并没有断开,依然保持关联
colors[4][0].name = 'Tony'
console.log(colors)
console.log(copyColors)
['red', 'orange', 'blue', 'yellow', [{name: 'Tony'}]]
['xxx', 'orange', 'blue', 'yellow', [{name: 'Tony'}]]
2、使用 Object.assign
代码如下
let colors = ['red', 'orange', 'blue', 'yellow', [{name: 'Jack'}]]
let copyColors = Object.assign({}, colors )
copyColors[0] = 'xxx'
console.log(colors)
console.log(copyColors)
// 可以看到第一层断开了
['red', 'orange', 'blue', 'yellow', [{name: 'Jack'}]]
['xxx', 'orange', 'blue', 'yellow', [{name: 'Jack'}]]
// 可以看到第二层并没有断开,依然保持关联
colors[4][0].name = 'Tony'
console.log(colors)
console.log(copyColors)
['red', 'orange', 'blue', 'yellow', [{name: 'Tony'}]]
['xxx', 'orange', 'blue', 'yellow', [{name: 'Tony'}]]
2. 什么是深拷贝
任意层修改都不会影响双方,彻底断开联系,各维护各的,实现真正的‘复制’
深拷贝有几种方式可以实现。
1、使用 JSON.stringify
(有缺陷:函数会被过滤掉)
代码如下
let colors = ['red', 'orange', 'blue', 'yellow', [{name: 'Jack', getName() {}}]]
let copyColors = JSON.parse(JSON.stringify(colors))
copyColors[0] = 'xxx'
console.log(colors)
console.log(copyColors)
// 可以看到第一层断开了,但函数被过滤掉了。
['red', 'orange', 'blue', 'yellow', [{name: 'Jack'}]]
['xxx', 'orange', 'blue', 'yellow', [{name: 'Jack'}]]
// 可以看到第二层也断开了
colors[4][0].name = 'Tony'
console.log(colors)
console.log(copyColors)
['red', 'orange', 'blue', 'yellow', [{name: 'Jack'}]]
['xxx', 'orange', 'blue', 'yellow', [{name: 'Tony'}]]
2、递归拷贝(深拷贝最稳妥方案)
代码如下
function deepClone(obj) {
let newObj = Array.isArray(obj) ? [] : {}
if (obj && typeof obj === "object") {
for (let key in obj) {
if (obj.hasOwnProperty(key)) {
newObj[key] = (obj && typeof obj[key] === 'object') ? deepClone(obj[key]) : obj[key];
}
}
}
return newObj
}
let colors = ['red', 'orange', 'blue', 'yellow', [{name: 'Jack'}]]
let copyColors = deepClone(colors)
copyColors[0] = 'xxx'
console.log(colors)
console.log(copyColors)
// 可以看到第一层断开了,函数也保留了。
['red', 'orange', 'blue', 'yellow', [{name: 'Jack', getName: ƒ getName()}]]
['xxx', 'orange', 'blue', 'yellow', [{name: 'Jack', getName: ƒ getName()}]]
// 可以看到第二层也断开了
colors[4][0].name = 'Tony'
console.log(colors)
console.log(copyColors)
['red', 'orange', 'blue', 'yellow', [{name: 'Jack', getName: ƒ getName()}]]
['xxx', 'orange', 'blue', 'yellow', [{name: 'Tony', getName: ƒ getName()}]]
3. 为什么存在浅拷贝?用深拷贝不行吗?
有这个疑惑的小伙伴给你点个赞,说明你对浅拷贝和深拷贝已经有了自己的见解。
首先这是关于到性能方面问题,比如有以下情况
-
有些人就想引用类型要第一层断开,剩下的 n 层保持关联呢,这种需求本人也常用过,你说 JS 能擅作主张吗?所以选择权还是在开发者手上,JS 不可能全部断开重新分配空间,它能尽量共用一份地址就共用。
-
我们都知道通过 dom 提供的 api 可以操控数据,比如
document.getElementsByTagName[0].innerHTML
可以直接更新试图数据,而且我们也知道document.getElementsByTagName
返回的是一个数组(是个伪数组来着,可通过 Array.from 或 […] 方式转为真正的数组),而数组属于引用类型,那我们就来试试对它们进行深拷贝 / 浅拷贝看看有什么效果。
首先是深拷贝,代码如下:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
</head>
<body>
<a>hello</a>
<a>world</a>
<a>!</a>
<button onclick="onClone()">深拷贝</button>
<script type="text/javascript">
// 克隆点击事件
function onClone() {
const newObj = deepClone(Array.from(document.getElementsByTagName('a')))
console.log('输出:', newObj)
}
// 深拷贝函数
function deepClone(obj) {
let newObj = Array.isArray(obj) ? [] : {}
if (obj && typeof obj === "object") {
for (let key in obj) {
if (obj.hasOwnProperty(key)) {
newObj[key] = (obj && typeof obj[key] === 'object') ? deepClone(obj[key]) : obj[key];
console.log('添加:', obj[key])
}
}
}
return newObj
}
</script>
</body>
</html>
效果图
可以明显看到,深拷贝过程中添加 3 个 a,可到了输出时显示的却是 3 个空对象,这说明深拷贝对 dom 无效。不过 JS 也提供了 element.cloneNode()
方法可以专门克隆节点,其中传递 true 表示深拷贝,不传表示浅拷贝,你看连官方 API 都引出了浅拷贝/深拷贝的概念,为什么会这样?其实也很简单,假设 dom 有几百层,而你只要克隆第一层,那用深拷贝还是浅拷贝呢? 明显是浅拷贝最合适了,所以这也是关于性能问题,JS 不能帮你做主。
下面我们来看看浅拷贝:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
</head>
<body>
<a>hello</a>
<a>world</a>
<a>!</a>
<button onclick="onChange()">浅拷贝</button>
<script type="text/javascript">
function onChange() {
const newObj = [...Array.from(document.getElementsByTagName('a'))]
console.log(newObj)
}
</script>
</body>
</html>
效果图
看到没?dom 支持浅拷贝 ,是不是很 surprised? 我们知道每个标签里有很多属性,每个属性也会包含一些引用类型,但浅拷贝也只是拷贝一层,并不会全部断开连接,或许这就是 JS 可以接受浅拷贝 dom 的原因。
有关于浅拷贝这方面想法的同学欢迎下方留言。
好了,内容就到这里。