1. 前言
这几天估计是嗑书嗑疯了,晚上死活睡不着估计神经衰弱,今天就不做react-native专栏了。正好前几天面试被问到深拷贝的问题,今天就做个专栏讨论吧。
2.深拷贝的几个实现方式:
1. JSON实现方式
这个是大家都知道的一种方式,不过它有三点问题:
var obj = {
name:"foo",
fun:function(){},
obj:obj,
age:undefined
}
let cloned_obj = JSON.parse(JSON.stringify(obj));
//结果就是{name: "foo"}
从上面的例子可以知道:
- function无法被复制
- 循环引用直接被移除.
- undefined将被移除
emmmmm,果然简单的东西大部分都有坑.
2. Object.create
我也听说过这个方法,不过嘛Object.create并不能复制一个对象,只不过是调用对象的constructor重新构造一个对象.
3.DIY
有这么多的坑,所以咱们还是自己动手丰衣足食吧……
class Utils {
constructor() {
this.map = new WeakMap();
}
deepCopy=(obj)=> {
let { map ,deepCopy} = this;
//如果是基本类型则直接返回
if (typeof obj !== "object") {
return obj;
}
//如果是引用类型,则判断是否是循环引用的对象
if (map.get(obj)) {
return {};
}
//如果是null
if (!obj) {
return null;
}
//此时细分类型
const obj_type = Object.prototype.toString.call(obj);
let temp_obj = null;
//如果是数组则迭代deepcopy
if (obj_type.includes("Array")) {
//缓存obj,留待下次判断是否循环引用
map.set(obj, obj_type);
temp_obj = [];
obj.forEach((item) => {
temp_obj.push(deepCopy(item));
});
}
//如果是对象的话,迭代deepcopy直到基本类型
if (obj_type.includes("Object")) {
map.set(obj, obj_type);
temp_obj = {};
Reflect.ownKeys(obj).forEach((key) => {
temp_obj[key] = deepCopy(obj[key]);
});
}
return temp_obj;
}
}
let u = new Utils();
console.log(u.deepCopy(sourceObj));
/**********************结果**************************
{fun: ƒ, name: "fun", version: null, arrs: Array(4)}
arrs: (4) [undefined, 1, 2, "3"]
fun: ƒ ()
name: "fun"
version: null
__proto__: Object
****************************************************/
唔,总算是清净了~
另外附上一个打印对象所有属性的方法,调试用的:
function obj2Str(obj = {}) {
let curItem = null;
let itemObj = {};
Reflect.ownKeys(obj).forEach((key) => {
curItem = obj[key];
//生成检测对象类型
const itemType = Object.prototype.toString.call(curItem);
if (itemType.includes("Null")) {
itemObj[key] = "null";
}
else if (itemType.includes("Undefined")) {
itemObj[key] = "undefined";
}
else if (itemType.includes("Array")) {
let arrayStr = "[";
curItem.forEach((item) => {
arrayStr += obj2Str(item);
});
arrayStr += "]";
itemObj[key] = arrayStr;
}
else if (itemType.includes("Object")) {
itemObj[key] = obj2Str(curItem);
}
else {
itemObj[key] = curItem.toString();
}
});
return JSON.stringify(itemObj);
}
3.后记
这个功能看似简单但是实际上有很多的坑:
类型判断
的方式选用:
1.typeof
形如 var x = “xx”; typeof x == ‘string’ typeof(x);
返回类型有:‘undefined’ “string” ‘number’ ‘boolean’ ‘function’ ‘object’
缺点:对于object类型不能细分是什么类型
优点:对空null的判断 'undefined’的应用
2.instanceof
形如 var d = new String(‘test’); d instanceof String ==true;
返回的类型有:String Number Boolean Function Object Array Date
优点:能区分出更细的类型如 Date Array 如 var num = 3; num instanceof Number 能返回具体的类型
缺点:直变量不能区分 必须采用new 的对象
3.constructor
形如:var x = []; x.constructor==Array;
优点:可以返回继承的类型
缺点: 不能对象的细分,如继承 必须手动修正
4.Object.prototype.toString.call();
优点:通用,返回"[objectString]" 具体object的类型
缺点:不能返回继承的类型
- 循环引用的问题
要解决这个问题就涉及到对真实数据的取舍,基于深拷贝的初衷–和原数据脱离关系
以及循环引用本身就是个错误的做法
,所以我还是选择了置undefined
不过最终咱们还是自己解决了这个问题,而且自己写的东西以后还能根据项目实际需求再做调整不是吗??