阅读原文 :【技术分享】你真的懂前端数据拷贝吗?
点击关注“八戒技术团队”,阅读更多技术干货
在我们的日常开发中,常常会涉及到数据的拷贝。在拷贝数据时,要保证新数据的独立性,不会互相影响,这涉及到数据的深浅拷贝。那如何正确的拷贝一个对象呢?
浅拷贝与深拷贝
浅拷贝是创建一个新对象,新旧对象共享同一个内存。如果拷贝是原始数据类型,那拷贝就是值。如果是引用类型,那拷贝的是内存地址,如果原数据进行了改变,新数据也会进行改变。
深拷贝是将数据完整的从内存中拷贝出来,在内存中开辟出新的存储区域。新旧数据的变化不会互相影响。深拷贝就是创建了一个新的数据,其值和原数据一致。
拷贝例子:
浅拷贝时,我们可以看见,原数据的ID值改变,新数据的ID没有变化。但原数据的引用类型变化,新数据的引用类型也变化了。
深拷贝时,我们可以看见,原数据的ID值改变,新数据的ID没有变化。原数据的引用类型变化,新数据的引用类型也没有。新数据是独立的。
浅拷贝方式
1.运算符...
2. Object.assign()
3. 数组concat()
4. 数组slice()
深拷贝方式
JSON.parse(JSON.stringify())
使用JSON.stringify()转换成JSON字符串,再使用JSON.parse()解析成新的对象,生成新的存储空间,从而实现深拷贝。
注意:
function Person (name) {
this.name = name;
}
let obj = {
id: 1,
childObj: {
id: 11
}
func () {},
und: undefined,
date: new Date(),
err: new Error(),
reg: /0-9/,
nan: NaN,
num: Infinity,
num2: -Infinity,
name: new Person('张三'),
sy: Symbol('sy'),
sets: new Set([ 1,2,3,4,5 ]),
maps: new Map()
}
// 循环引用报错
let obj1 = { id: 13 }
// obj.obj1 = obj1;
// obj1.obj = obj;
let newObj = JSON.parse(JSON.stringify(obj));
console.log(obj)
// {
// id: 1,
// childObj: { id: 11 },
// func: ƒ func(),
// und: undefined,
// date: Sun Mar 13 2022 12:07:22 GMT+0800 (中国标准时间) {},
// err: Error at,
// reg: /0-9/,
// nan: NaN,
// num: Infinity,
// num2: -Infinity,
// name: new Person('张三'),
// sy: Symbol(sy),
// sets: Set(5) {1, 2, 3, 4, 5},
// maps: Map(0) {size: 0}
// }
console.log(newObj)
// {
// id: 1,
// childObj: { id: 11 },
// date: '2022-03-13T03:51:40.492Z',
// err: {},
// id: 1,
// nan: null,
// num: null,
// num2: null
// name: {name: '张三'}
// sy: {},
// sets: {}
// }递归循环
采用递归遍历每一项,针对特殊的数据类型,进行单独处理,使用object.prototype.toString获取数据类型。
使用map,避免循环引用,无法正确拷。
// 判断数据类型
const dataType = (target) => {
let type = Object.prototype.toString.call(target).slice(8, -1);
let typeObj = {
'Object': true,
'Array': true,
'Function': true,
'RegExp': true,
'Date': true,
'Error': true,
'Arguments': true,
'Set': true,
'Map': true,
'Promise': true,
'Symbol': true,
'BigInt': true,
'Math': true,
'Null': true,
'Undefined': true,
'String': true,
'Number': true,
'Boolean': true
};
let val = {
type
};
val['is' + type] = typeObj[type];
if(type === 'String') {
try {
let obj = JSON.parse(target);
val['isJSON'] = obj && typeof obj == 'object' ? true: false;
}catch(e) {
val['isJSON'] = false;
}
}
return val;
}
const deepCopy = (data) => {
const mapObj = new Map(); // 处理循环引用
const copy= (data) => {
// 基本数据类型直接返回
let types = [ 'String', 'Number', 'BigInt', 'Boolean', 'Undefined', 'Null' ];
if(types.includes(dataType(data).type)) return data;
// 函数
if(dataType(data).isFunction) {
return new Function('return ' + data.toString()).call(this)
}
// symbol
if(dataType(data).isSymbol) {
return Object(Symbol.prototype.valueOf.call(data));
}
// 时间
if(dataType(data).isDate) {
return new Date(data.valueOf())
}
// 正则
if(dataType(data).isRegExp) {
const reFlags = /\w*$/;
const result = new data.constructor(data.source, reFlags.exec(data));
result.lastIndex = data.lastIndex;
return result;
}
// 引用数据类型特殊处理
const newObj = new data.constructor();
// 判断处理循环引用
if (mapObj.has(data)) {
return mapObj.get(data);
}
// 不存在则第一次设置
mapObj.set(data, newObj);
// Map
if(dataType(data).isMap) {
data.forEach((v,k) => {
newObj.set(k, copy(v))
})
return newObj;
}
// Set
if(dataType(data).isSet) {
for(let val of data.values()) {
newObj.add(copy(val))
}
return newObj;
}
// 对象 或 数组
if(dataType(data).isObject || dataType(data).isArray ){
for(let key in data) {
newObj[key] = copy(data[key])
}
return newObj;
}
// 其它类型直接返回
return data;
}
return copy(data);
}使用:
let obj1 = { id: 13 }
// 构造函数
function Person (name) {
this.name = name;
}
let obj = {
id: 1,
childObj: {
id: 11
},
func () {},
und: undefined,
date: new Date(),
err: new Error(),
reg: /0-9/,
nan: NaN,
num: Infinity,
num2: -Infinity,
name: new Person('张三'),
sy: Symbol('sy'),
bn: BigInt(123456897)
}
obj.obj1 = obj1;
obj1.obj = obj;
let newObj = deepCopy (obj);
console.log(newObj)
// {
// bn: 123456897n
// id: 1,
// childObj: { id: 11 },
// func: ƒ func(),
// und: undefined,
// date: Sun Mar 13 2022 12:07:22 GMT+0800 (中国标准时间) {},
// err: Error at,
// reg: /0-9/,
// nan: NaN,
// num: Infinity,
// num2: -Infinity,
// name: Person {name: '张三'},
// sy: Symbol {Symbol(sy), description: 'sy'},
// obj1:{id: 13, obj: {…}},
//}以上就是我对深浅拷贝的理解,以及如何进行深拷贝,进行的封装处理。目前深拷贝,没有对大型数据拷贝做处理,因为数据一旦过大,那么递归处理的时间就会增加。那么这一块还可以继续研究。希望这篇文章能在你的工作为你提供帮助。
希望以上内容能对有需要的人有所帮助
欢迎大家留言写下自己希望了解的技术方向
欢迎大家一起探讨交流
| 留言与评论(共有 0 条评论) “” |