说深拷贝之前先说一下js中的几种判断类型的方式
- 判断类型首选Object原型上的toString方法执行时传入当前要判断的数据
const value = function(){};
Object.prototype.toString.call(value).slice(8, -1).toLowerCase();
- 使用value.construtor.name属性来判断
const value = "123";
value.constructor.name.toLowerCase();
const val = 123;
typeOf value
const value = '123;
value.instanceOf(String) // true
const arr = [];
Array.isArray(arr)
深拷贝
- JSON.parse(JSON.stringify())
- 循环引用问题
- 属性为function和undefined会丢失
- Symbol值会生成undefined
- 时间对象会生成时间字符串
- 正则或者错误对象会生成空对象
- 会丢失对象的构造器
- 结构化克隆structuredClone(web原生能力)
- 兼容性问题
- 解决了JSON.parse(JSON.stringify())大部分问题
- 存在的问题:丢失对象的原型链,不可拷贝函数,不可拷贝dom节点与Symbol
- MessageChannel
- 兼容性问题
- MessageChannel是一个红任务,好在其执行时机先于定时器
- 利用MessageChannel序列化进行深拷贝
- 存在的问题:丢失对象的原型链,不可拷贝函数,不可拷贝dom节点与Symbol
function deepClone(obj) {
return new Promise((resolve, reject) => {
try {
const { port1, port2 } = new MessageChannel();
port2.onmessage = function (e) {
resolve(e.data);
};
port1.postMessage(obj);
} catch (err) {
reject(err);
}
});
}
const b = {
a: undefined,
b: {
c: 1,
},
};
console.log(b);
deepClone(b).then((res) => {
console.log(res, "res====");
});
function isTypeOf(value) {
return Object.prototype.toString.call(value).slice(8, -1).toLowerCase();
}
function deepCopy(value) {
if (!value || typeof value !== "object") {
return value;
}
let cache = null;
if (!deepCopy.cache) {
deepCopy.cache = new WeakMap();
}
cache = deepCopy.cache;
if (cache.has(value)) {
return cache.get(value);
}
if (value.nodeType && "cloneNode" in value) {
return value.cloneNode(true);
}
if (isTypeOf(value) === "date") {
return new Date(value.getTime());
}
if (isTypeOf(value) === "regexp") {
return new RegExp(value);
}
if (isTypeOf(value) === "error") {
return new Error(value.message);
}
if (isTypeOf(value) === "set") {
const set = new Set();
cache.set(value, true);
value.forEach((item) => {
set.add(deepCopy(item));
});
return set;
}
if (isTypeOf(value) === "map") {
const map = new Map();
cache.set(value, true);
value.forEach((item, key) => {
map.set(key, deepCopy(item));
});
return map;
}
const result = Array.isArray(value)
? []
: value.construtor
? new value.construtor()
: {};
cache.set(value, true);
for (let key in value) {
result[key] = deepCopy(value[key]);
}
return result;
}
结语
- 一般情况下对于不是很复杂的数据结构,可以使用es6的扩展运算符或者一些数组方法来进行拷贝
- 在项目中可以自己来封装深拷贝方法 避免引入第三方库 可以减少打包体积
- 深拷贝可以解决对象引用问题 但是占用较大内存也是一个需要认真考虑的事情
- 在react项目中我们可以使用一些不可变数据结构的库来避免深拷贝带来的内存消耗问题,如immer.js