首先明确 javaScript 中简单数据类型由于变量中保存的是数据的字面量,因此只需用’='赋值的方式进行拷贝即可。javaScript 的深浅拷贝只针对于复杂数据类型,例如数组、对象。
使用’='拷贝存在的问题
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<script>
// 数组类型
const arr = [1, 2, 3]
// 直接赋值
const newArr = arr
console.log(newArr) // [1, 2, 3]
// 改变newArr中的数据
newArr[0] = 4
console.log(arr) // [4, 2, 3]
// 对象类型
const obj = {
name: '小明',
age: 18
}
// 直接赋值
const newObj = obj
console.log(newObj) // {name: '小明', age: 18}
// 改变newObj中的数据
newObj.age = 20
console.log(obj) // {name: '小明', age: 20}
</script>
</body>
</html>
当使用’='赋值的方式进行复杂数据类型的拷贝时,可以得到该数据的副本,当对副本进行修改时,原数据也会发生改变,这是因为它们保存的是数据的地址。
浅拷贝
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<script>
// 数组类型
const arr = [1, 2, 3]
// 浅拷贝方式一:
const newArr = Array.prototype.concat(arr)
console.log(newArr) // [1, 2, 3]
// 改变newArr中的数据
newArr[0] = 4
console.log(arr) // [1, 2, 3]
// 浅拷贝方式二:
const newArr2 = [...arr]
console.log(newArr2) // [1, 2, 3]
// 改变newArr2中的数据
newArr2[0] = 5
console.log(arr) // [1, 2, 3]
// 对象类型
const obj = {
name: '小明',
age: 18
}
// 浅拷贝方式一:
const newObj = {}
Object.assign(newObj, obj)
console.log(newObj) // {name: '小明', age: 18}
// 改变newObj的数据
newObj.age = 20
console.log(obj) // {name: '小明', age: 18}
// 浅拷贝方式二:
const newObj2 = {...obj}
console.log(newObj2) // {name: '小明', age: 18}
// 改变newObj2的数据
newObj2.age = 30
console.log(obj) // {name: '小明', age: 18}
</script>
</body>
</html>
数组和对象的两种浅拷贝方式,不仅能够拷贝出原数据,并且修改数据不改变原数据,这是因为浅拷贝是拷贝的地址。但是浅拷贝依然存在一个问题,如下:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<script>
// 数组类型
const arr = [1, 2, [3, 4]]
// 浅拷贝:
const newArr = Array.prototype.concat(arr)
console.log(newArr) // [1, 2, [3, 4]]
// 改变newArr中的数据
newArr[2][0] = 5
console.log(arr) // [1, 2, [5, 4]]
// 对象类型
const obj = {
name: '小明',
age: 18,
family: {
mother: 'mom',
father: 'dad'
}
}
// 浅拷贝:
const newObj = {}
Object.assign(newObj, obj)
console.log(newObj) // {name: '小明', age: 18, family: {mother: 'mom', father: 'dad'}}
// 改变newObj的数据
newObj.family.mother = 'mom2'
console.log(obj) // {name: '小明', age: 18, family: {mother: 'mom2', father: 'dad'}}
</script>
</body>
</html>
当数组或是对象中又嵌套了数组或对象,改变这里面的值依然会影响原数据。
深拷贝
深拷贝,拷贝的是对象本身,而不是地址了,因此,无论嵌套了多少层,对其修改都不会影响原数据。
这里介绍三种深拷贝的方式:
1、利用递归自定义深拷贝函数
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<script>
// 递归实现深拷贝
function deepCopy(newObj, oldObj) {
for (let k in oldObj) {
if (oldObj[k] instanceof Array) {
newObj[k] = []
deepCopy(newObj[k], oldObj[k])
} else if (oldObj[k] instanceof Object) {
newObj[k] = {}
deepCopy(newObj[k], oldObj[k])
} else {
newObj[k] = oldObj[k]
}
}
}
// 数组类型
const arr = [1, 2, [3, 4]]
// 深拷贝
const newArr = []
deepCopy(newArr, arr)
console.log(newArr) // [1, 2, [3, 4]]
// 改变newArr中的数据
newArr[2][0] = 5
console.log(arr) // [1, 2, [3, 4]]
// 对象类型
const obj = {
name: '小明',
age: 18,
family: {
mother: 'mom',
father: 'dad'
}
}
// 深拷贝
const newObj = {}
deepCopy(newObj, obj)
console.log(newObj) // {name: '小明', age: 18, family: {mother: 'mom', father: 'dad'}}
// 改变newObj中的数据
newObj.family.mother = 'mom2'
console.log(obj) // {name: '小明', age: 18, family: {mother: 'mom', father: 'dad'}}
</script>
</body>
</html>
2、利用lodash库中的cloneDeep方法实现深拷贝
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<script src="js/lodash.min.js"></script>
<script>
// 数组类型
const arr = [1, 2, [3, 4]]
// 深拷贝
const newArr = _.cloneDeep(arr)
console.log(newArr) // [1, 2, [3, 4]]
// 改变newArr中的数据
newArr[2][0] = 5
console.log(arr) // [1, 2, [3, 4]]
// 对象类型
const obj = {
name: '小明',
age: 18,
family: {
mother: 'mom',
father: 'dad'
}
}
// 深拷贝
const newObj = _.cloneDeep(obj)
console.log(newObj) // {name: '小明', age: 18, family: {mother: 'mom', father: 'dad'}}
// 改变newObj中的数据
newObj.family.mother = 'mom2'
console.log(obj) // {name: '小明', age: 18, family: {mother: 'mom', father: 'dad'}}
</script>
</body>
</html>
3、通过JSON.stringify()实现深拷贝
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<script src="js/lodash.min.js"></script>
<script>
// 数组类型
const arr = [1, 2, [3, 4]]
// 深拷贝
const newArr = JSON.parse(JSON.stringify(arr))
console.log(newArr) // [1, 2, [3, 4]]
// 改变newArr中的数据
newArr[2][0] = 5
console.log(arr) // [1, 2, [3, 4]]
// 对象类型
const obj = {
name: '小明',
age: 18,
family: {
mother: 'mom',
father: 'dad'
}
}
// 深拷贝
const newObj = JSON.parse(JSON.stringify(obj))
console.log(newObj) // {name: '小明', age: 18, family: {mother: 'mom', father: 'dad'}}
// 改变newObj中的数据
newObj.family.mother = 'mom2'
console.log(obj) // {name: '小明', age: 18, family: {mother: 'mom', father: 'dad'}}
</script>
</body>
</html>
总结
1、浅拷贝的方式:
数组:Array.prototype.concat()、[…arr]
对象: Object.assign()、{…obj}
2、深拷贝的方式:
递归实现:
function deepCopy(newObj, oldObj) {
for (let k in oldObj) {
if (oldObj[k] instanceof Array) {
newObj[k] = []
deepCopy(newObj[k], oldObj[k])
} else if (oldObj[k] instanceof Object) {
newObj[k] = {}
deepCopy(newObj[k], oldObj[k])
} else {
newObj[k] = oldObj[k]
}
}
}
引入lodash库调用cloneDeep()函数实现;
通过JSON.parse(JSON.stringify())实现