1.浅拷贝
概念
对数据拷贝的时候只拷贝一层,深层次的只拷贝了地址
代码如下(示例):
const obj = { a: 1, goods: { name: '鞋', price: 199 } } const b = { ...obj }
问题
const obj = {
a: 1,
goods: { name: '鞋', price: 199 }
}
const b = { ...obj }
b.goods.name = '球鞋'
我们可以通过代码来看一下,当我们拷贝过数据后,去修改拷贝的数据
会发现通过浅拷贝更深层次的引用类型,如果修改 b.googs,最终 obj.goods 也会跟着修改,是因为在拷贝的时候,我们只是将引用地址拷贝给了 b.goods,也就是说 b.goods 和 ob.goodsj 引用的是同一个对象
也可以用 Object.assign()实现浅拷贝
2.深拷贝
JSON 方法实现深拷贝
我们可以先利用一个简单方式来实现深拷贝
var obj = {
a: 1,
c: {
c1: 1,
c2: 2
}
}
var str = JSON.stringify(obj)
var b = JSON.parse(str)
// 修改 obj的属性
obj.c.c1 = 2
console.log(b)
我们先将需要拷贝的代码利用 JSON.stringify 转成字符转,然后再利用
JSON.parse 将字符转转回对象,即完成拷贝
但是拷贝如下数据
var obj = {
a: /^1(?:3\d|4[4-9]|5[0-35-9]|6[67]|7[013-8]|8\d|9\d)\d{8}$/,
b: new Date(),
c: null,
d: Symbol(1),
sing: function() {
console.log('唱歌')
},
id: 1,
name: 'andy',
msg: {
age: 18
},
color: ['pink', 'red']
}
问题:
- 造成数据丢失和数据异常
- function、undefined 直接丢失
- NaN、Infinity 和-Infinity 变成 null
RegExp
、Error
对象只得到空对象;
总结:
可以看到,两者都是浅拷贝。 Object.assign()方法接收的第一个参数作为目标对象,后面的所有参数作为源对象。然后把所有的源对象合并到目标对象中。它会修改了一个对象,因此会触发 ES6 setter。
扩展操作符(…)使用它时,数组或对象中的每一个值都会被拷贝到一个新的数组或对象中。它不复制继承的属性或类的属性,但是它会复制ES6的 symbols 属性。
递归深拷贝
递归实现的思路是什么样的?我们来分析一下
-
我们肯定要定义一个方法,那么这个方法最终应该返回一个深拷贝的数据
-
既然要返回一个数据,我们首先就要定义一个数据,但是数据是对象还是数组?所以需要判断,如果要拷贝的数据是数组,即定义一个数组,如果是一个对象,即定义一个对象
-
方法里面怎么拷贝啊?还是一样的利用 for in 循环,在循环内部,需要判断,如果是类型是简单类型,直接拷贝,如果是引用类型,就需要在一次的将引用类型里面的值取出来
根据上面的逻辑我们处理一下代码
var obj = {
a: 1,
c: {
c1: 1,
c2: 2
}
}
function deepCopy(obj) {
// 判断拷贝的数据是对象还是数组 生成定义的数据
var copy = Array.isArray(obj) ? [] : {}
for (key in obj) {
// 循环的时候如果此项为引用类型,需要 在一次的将引用类型里面的值取出来
if (typeof obj[key] == 'object') {
// 再次调用该方法取数据
copy[key] = deepCopy(obj[key])
} else {
copy[key] = obj[key]
}
}
return copy
}
var b = deepCopy(obj)
console.log(b)
但是递归也会遇到上面同样的问题
#数据丢失和异常处理
处理函数 Symbol 正则 Error 等数据类型正常拷贝
// 日期格式
if (obj instanceof Date) {
return new Date(obj)
}
// Symbol
if (obj instanceof Symbol) {
return new Symbol(obj)
}
// 函数
if (obj instanceof Function) {
return new Function(obj)
}
// 正则
if (obj instanceof RegExp) {
return new RegExp(obj)
}
循环引用问题
数据自己引用自己,此时拷贝就会进入死循环
var obj = {
a: /^1(?:3\d|4[4-9]|5[0-35-9]|6[67]|7[013-8]|8\d|9\d)\d{8}$/,
b: new Date(),
c: null,
d: Symbol(1),
sing: function() {
console.log('唱歌')
},
id: 1,
name: 'andy',
msg: {
age: 18
},
color: ['pink', 'red']
}
obj.obj1 = obj
解决思路:
将每次拷贝的数据进行存储,每次在拷贝之前,先看该数据是否拷贝过,如果拷贝过,直接返回,不在拷贝,如果没有拷贝,对该数据进行拷贝并记录该数据以拷贝
1、使用数组
function cloneDeep3(source, uniqueList) {
if (!isObject(source)) return source
if (!uniqueList) uniqueList = [] // 新增代码,初始化数组
var target = Array.isArray(source) ? [] : {}
// 要是有 别再循环拷贝了 直接返回 该值
var uniqueData = find(uniqueList, source)
if (uniqueData) {
return uniqueData.target
}
// 数据不存在,保存源数据,以及对应的引用
uniqueList.push({
source: source,
target: target
})
// =============
for (var key in source) {
if (Object.prototype.hasOwnProperty.call(source, key)) {
if (isObject(source[key])) {
target[key] = cloneDeep3(source[key], uniqueList) // 新增代码,传入数组
} else {
target[key] = source[key]
}
}
}
return target
}
// 新增方法,用于查找
function find(arr, item) {
for (var i = 0; i < arr.length; i++) {
if (arr[i].source === item) {
return arr[i]
}
}
return null
}
// 用上面测试用例已测试通过
-
使用 map 数据:强引用,无法被垃圾回收
function deepCopy(obj, map = new Map()) { if (!isObject(obj)) return var newObj = Array.isArray(obj) ? [] : {} if (map.get(obj)) { // 读取要拷贝的数据 return map.get(obj) // 要是有 别再循环拷贝了 直接返回 该值 } map.set(obj, newObj) // 存拷贝的数据 for (var key in obj) { if (obj.hasOwnProperty(key)) { if (isObject(obj[key])) { newObj[key] = deepCopy5(obj[key], map) } else { newObj[key] = obj[key] } } } return newObj }
-
使用 hash 表:弱引用,可被垃圾回收
function cloneDeep3(source, hash = new WeakMap()) { if (!isObject(source)) return source if (hash.has(source)) return hash.get(source) // 读取要拷贝的数据 // 要是有 别再循环拷贝了 直接返回 该值 var target = Array.isArray(source) ? [] : {} hash.set(source, target) // 新增代码,哈希表设值 for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { if (isObject(source[key])) { target[key] = cloneDeep3(source[key], hash) // 新增代码,传入哈希表 } else { target[key] = source[key] } } } return target }
lodash
使用 lodash 实现深拷贝
1.下包
npm i lodash
2.引入
import lodash from 'lodash'
3.实现深拷贝
const obj1 = lodash(obj)