Open H246802 opened 5 years ago
第一种
function deepCopyArray(arr) {
if(!Array.isArray(arr)){
console.log(`${arr}不是数组`)
return
}
let result = []
for (let i = 0; i < arr.length; i++){
if(typeof arr[i] === "number" || typeof arr[i] === "boolean" || typeof arr[i] === "string"){
result[i] = arr[i]
} else if(typeof arr[i] === "object" && Array.isArray(arr[i])){
result[i] = deepCopyArray(arr[i])
}
}
return result
}
第二种
// 使用JSON 转成 json 类型 string 后再转成对象
// 部分bug:函数类型直接消失、时间类型会变成字符串...
// 依靠上述代码,我们进行优化,包括对象的深度遍历
// 同时对自身递归时使用闭包代码更高效
function deepCopy(obj) {
return (function copy(value) {
if (
typeof value === "object" &&
value !== null &&
!(value instanceof Boolean) &&
!(value instanceof Date) &&
!(value instanceof Number) &&
!(value instanceof RegExp) &&
!(value instanceof String)
) {
var res;
if (Array.isArray(value)) {
res = [];
value.forEach((ele, i) => {
res[i] = copy(ele);
});
} else {
res = {};
Object.keys(value).forEach(function(name) {
res[name] = copy(value[name]);
});
}
return res
}
return value;
})(obj);
}
关于数组的循环引用
可以参考 cycle.js 原理是将循环的那部分“路径”通过变量缓存起来,如果使用上方的 深拷贝函数,可能会导致栈堆溢出,程序卡死
// 仿照 cycle.js 写了一个解决循环引用的
if (typeof JSON.decycle !== "function") {
JSON.decycle = function decyle(object) {
"use strict";
//制作一个对象或数组的深层副本,确保最多存在
// 一个结果结构中对象或数组的实例。该
// 重复引用(可能正在形成循环)被替换为
// {“$ ref”:PATH}
// path 为 $的子属性 $[x] or $[x][y] ...
// 到最后进行解析
//生成字符串'[{“$ ref”:“$”}]'。
// JSONPath用于定位唯一对象。 $表示最高级别
//对象或数组。
var objects = new WeakMap(); // 为了提供判断是否是循环引用的证据
return (function derez(value, path) {
// derez 提供深度拷贝
var old_path; // 最早出现的值的路径
var nu; // 最终copy结果return
// 判断传入的是否是对象或者数组
if (
typeof value === "object" &&
value !== null &&
!(value instanceof Boolean) &&
!(value instanceof Date) &&
!(value instanceof Number) &&
!(value instanceof RegExp) &&
!(value instanceof String)
) {
// 如果是数组或对象,先看看该对象是否在 objects
// 是否有该属性(这也是闭包的一个理由)
old_path = objects.get(value);
if (old_path !== undefined) {
return { $ref: old_path };
}
// 当改对象不存在weakmap属性中时,则设置该值
objects.set(value, path);
if (Array.isArray(value)) {
nu = [];
value.forEach((element, i) => {
nu[i] = derez(element, `${path}[${i}]`);
});
} else {
nu = {};
Object.keys(value).forEach(name => {
nu[name] = derez(value[name], `${path}[${JSON.stringify(name)}]`);
});
}
return nu;
}
return value;
})(object, "$");
};
}
if (typeof JSON.retrocycle !== "function") {
JSON.retrocycle = function retrocycle($) {
"use strict";
// 正则判断是否路径符合 $[x][y]... 格式
// 因为我们是靠 $ 确定的位置
var px = /^\$(?:\[(?:\d+|"(?:[^\\"\u0000-\u001f]|\\(?:[\\"\/bfnrt]|u[0-9a-zA-Z]{4}))*")\])*$/;
// 恢复因为decycle函数减少的对象
(function rez(value) {
if (value && typeof value === "object") {
// 区分数组与对象
if (Array.isArray(value)) {
value.forEach((element, i) => {
if (typeof element === "object" && element !== null) {
var path = element.$ref;
if (typeof path === "string" && px.test(path)) {
// 一步一步借助于引用类型,第一层循环时 value === $
// 第二步value === $[i]
// ....
// 最后不断重复赋值下去,不返回新结果,传进的参数没有变化
value[i] = eval(path);
} else {
rez(element);
}
}
});
} else {
Object.keys(value).forEach(function(name) {
var item = value[name];
if (typeof item === "object" && item !== null) {
var path = item.$ref;
if (typeof path === "string" && px.test(path)) {
value[name] = eval(path);
} else {
rez(item);
}
}
});
}
}
})($);
return $;
};
}
总结一下
JavaScript的深拷贝还有许多的坑,还存在的问题有如何拷贝原型链上的属性?如何拷贝不可枚举属性? 如何拷贝Error对象等等的坑,不过日常生活中使用
JSON.parse(JSON.stringify(obj))
可以解决大部分问题,如果实在不行,还可以寻找第三方插件,完完全全考虑周全会把问题复杂化。
写一个深拷贝函数 deepCopyArray,实现数组的深拷贝
要求:
思考