深度拷贝
区别于浅拷贝:
浅拷贝:对不同数据类型表现不同,对于基本数据类型(布尔、数值、string、null、undefined、Symbol),拷贝基本数据类型的值;对于引用数据类型(Object、Array、Date、Error、Number、String、Boolean等),拷贝的是内存地址(其中一个变化会影响到另外一个元素)
深拷贝:主要是对于引用数据类型,新拷贝元素(每一层)都拥有一个新的内存空间,不会和原来的元素公用一块内存空间
策略模式
策略模式是为了避免过多的if else if else嵌套,方便代码扩展
参考链接: 深入浅出js中的策略模式
分析
分析,如何完成深度拷贝
(1) 区分基本数据和引用数据
(2) 从引用数据里分出 可遍历和不可遍历数据分开处理
(3)注意循环引用的问题
判断数据类型
// 区分是否为对象
const isObject = (o) => {
return (o !== null) && (typeof o === 'object' || typeof o === 'function');
}
// 准确判断引用数据类型
const typeTag = (o) => {
return Object.prototype.toString.call(o).slice(8, -1);
}
注: typeof 检测Symbol类型,返回symbol
const isObject = (o) => {
return o !== null && (typeof o === 'obejct' || typeof o === 'function');
}
const typeTag = (o) => {
return Object.prototype.toString
.call(o)
.slice(8,-1);
}
const cloneTarget = null;
const cloneArrayorObject = (target) => {
for (const key in target) {
cloneTarget[key] = clone(target[key], map);
}
return cloneTarget;
}
const cloneFunction = (target) => {
// 参数 ,函数体
const bodyReg = /(?<={)(.|\n)+(?=})/m;
const paramReg = /(?<=\().+(?=\)\s+{)/;
const funcString = target.toString();
if (target.prototype) {
const params = paramReg.exec(funcString);
const body = bodyReg.exec(funcString);
if (body) {
if (params) {
const paramArr = params[0].split(',');
return new Function(...paramArr, body[0]);
} else {
return new Function(body[0]);
}
} else {
return null;
}
} else {
return evel(funcString);
}
}
const cloneReg = (target) => {
let flags = /\w*$/;
let result = new RegExp(target.source, flags.exec(target));
result.lastIndex = target.lastIndex;
return result;
}
const cloneBase = (target) => {
return target.constructor(target);
}
const strategy = {
'Object': cloneArrayorObject,
'Array': cloneArrayorObject,
'Base': cloneBase,
'Date': function(o) {
return new Date(+o);
},
'RegExp': function(o) {
const flags = /\w*$/;
const result = new RegExp(o.source, flags.exec(o));
reult.lastIndex = o.lastIndex;
return result;
},
'Set': function(o) {
o.forEach((value,key) => {
cloneTarget.add(clone(value));
})
return cloneTarget;
},
'Map': function(o) {
o.forEach((value, key) => {
cloneTarget.set(key, clone(value));
})
return cloneTarget;
},
'Symbol': function(o) {
return Object(Symbol.prototype.valueOf.call(o));
},
'Function': cloneFunction,
}
const hasArg = ['Boolean', 'Nubmer', 'String', 'Error', 'Date'];
// 如果是数组
// weakmap 对象设置为键,没有Iterator,因为垃圾回收不知道什么时候进行,所以就不能进行遍历
// 缓存的时候用到
function clone (target, map = new WeakMap()){
if (!isObject(target)) {
return target;
}
let type = typeTag(target);
if (!hasArg.includes(type)) {
// 初始化cloneTarget
cloneTarget = new type();
} else {
type = 'base';
}
// 防止循环引用
if (map.get(target)) {
return target;
}
map.set(target,cloneTarget);
return strategy[type](target);
}
注:JSON.stringify和JSON.parse可以实现深度拷贝,但是注意1. 字符串化的时候,忽略undefined、function、正则、Symbol; 2. 不能循环引用 ,报错3. 对于数组、对象、Map、Set只能序列化可遍历属性 4.Date序列化的时候,转换成字符串
参考地址:
[1] 如何写出一个惊艳面试官的深拷贝
[2] JavaScript如何实现深拷贝
[3] JSON.stringify()

3238

被折叠的 条评论
为什么被折叠?



