我正在参与掘金创作者训练营第4期,点击了解活动详情,一起学习吧!
深浅拷贝
前阵子做一个图表可视化的项目,对每个类型的图表都配置了默认配置项(对象),一开始没考虑深拷贝问题,结果出篓子了...修改后面的图表,前面的被改了...那么今天就整理下深浅拷贝相关的内容。
浅拷贝
拷贝基本类型,对于对象类型,拷贝的是地址
Object.assign...展开运算符
深拷贝
解决对象引用问题
(一)JSON.parse(JSON.srtingify(obj))
- 局限性
- 会忽略
undefined - 会忽略
symbol - 不能序列化函数
- 不能解决循环引用的对象
- 会忽略
循环引用问题
let obj = {
a: 1,
b: {
c: 2,
d: 3,
}
}
obj.c = obj.b
obj.e = obj.a
obj.b.c = obj.c
obj.b.d = obj.b
obj.b.e = obj.b.c
let newObj = JSON.parse(JSON.stringify(obj))
console.log(newObj)
// Uncaught TypeError: Converting circular structure to JSON
序列化问题
在遇到函数、undefined 或者 symbol的时候,该对象也不能正常的序列化
JSON语法
- 支持数字、字符串、布尔值、null四种,不支持undefined
- NaN、Infinity和-Infinity序列化的结果是null
- 不支持函数
- 除了RegExp、Error对象,JSON语法支持其他所有对象
- 日期对象序列化的结果是字符串,并不会将其还原为日期对象
- 只能序列化对象的可枚举的自有属性
let a = {
age: undefined,
sex: Symbol('male'),
jobs: function() {}, name: 'merlin'
}
let b = JSON.parse(JSON.stringify(a))
console.log(b) // {name: "merlin"}
(二)MessageChannel
使用场景:拷⻉的对象含有内置类型并且不包含函数,可以处理 undefined 和循环引用对象
function structuralClone(obj) {
return new Promise(resolve => {
const { port1, port2 } = new MessageChannel();
port2.onmessage = ev => resolve(ev.data);
port1.postMessage(obj)
})
}
var obj = {
a: 1,
b: {
c: 2
}
}
obj.b.d = obj.b
// 注意该方法是异步的
const test = async () => {
const clone = await structuralClone(obj)
console.log(clone)
}
test()
(三)手写深拷贝函数
简单实现版
/**
* @description: 判断一个值是否是基本类型及函数
* @param {string} value 任何值
* @return {*}
*/
function isPrimitive(value: any) {
return /Number|Boolean|String|Null|Undefined|Symbol|Function/.test(
Object.prototype.toString.call(value)
);
}
/**
* @description: 深拷贝简易版,包含基础类型,函数,对象,数组
* @param {any} source 原数据
* @return {*}
*/
function deepCopy(source: any) {
// 基本类型及函数
if (isPrimitive(source)) {
return source;
}
// 判断是数组还是对象
let result = Array.isArray(source) ? [] : {};
for (let key in source) {
if (source.hasOwnProperty(key)) {
if (typeof source[key] === 'object') {
result[key] = deepCopy(source[key]); //递归复制
} else {
result[key] = source[key];
}
}
}
return result;
}
较详细版
- 处理复杂对象,如
Date、Regexp等 - 处理循环引用
/**
* @description: 判断一个值是否是基本类型及函数
* @param {string} value 任何值
* @return {*}
*/
function isPrimitive(value: any) {
return /Number|Boolean|String|Null|Undefined|Symbol|Function/.test(
Object.prototype.toString.call(value)
);
}
/**
* @description: 深拷贝函数,包括基础类型,函数,Map,Set,Date,Regex,引用类型等。
* @param {any} source 原数据
* @param {WeakMap} memory 记录临时值
* @return {*}
*/
function deepClone(source:any, memory: WeakMap<object, any> = new WeakMap()) {
let result = null;
// 原始数据类型及函数
if (isPrimitive(source)) {
result = source;
}
// 数组
else if (Array.isArray(source)) {
result = source.map((value) => deepClone(value, memory));
}
// 内置对象Date、Regex
else if (Object.prototype.toString.call(source) === "[object Date]") {
result = new Date(source);
} else if (Object.prototype.toString.call(source) === "[object Regex]") {
result = new RegExp(source);
}
// 内置对象Set、Map
else if (Object.prototype.toString.call(source) === "[object Set]") {
result = new Set();
for (const value of source) {
result.add(deepClone(value, memory));
}
} else if (Object.prototype.toString.call(source) === "[object Map]") {
result = new Map();
for (const [key, value] of source.entries()) {
result.set(key, deepClone(value, memory));
}
}
// 引用类型
else {
if (memory.has(source)) {
result = memory.get(source);
} else {
result = Object.create(null);
memory.set(source, result);
// Reflect.ownKeys()返回所有属性
// Object.keys()只能返回可枚举属性
Reflect.ownKeys(source).forEach((key) => {
const value = source[key];
result[key] = deepClone(value, memory);
});
}
}
return result;
}
参考链接