本文所用测试对象:
var obj = {
a: 1,
b: '1',
c: true,
d: [1,2,3],
e: function() {},
f: null,
g: undefined,
h: { aa: 2 }
}
1、JSON法
此方法最简单,但有一定的弊端,即undefined、null、function、symbol等会被忽略,转换后的对象或数组,会缺少对应的属性。
JSON.parse(JSON.stringify(obj));
通过测试结果可以看出e、f、g三个属性没了,所以此方法有一定的限制,下面来自己封装一个。
2、封装函数
2.1、初步封装
function clone(obj) {
var objClone = Array.isArray(obj) ? [] : {};
if (obj instanceof Object && typeof obj !== 'function') {
for (var k in obj) {
objClone[k] = clone(obj[k]);
}
} else {
objClone = obj;
}
return objClone;
}
clone(obj);
此次克隆所有属性都存在,大致完成。但这样封装会有一个问题,用for...in循环会把原型链里的属性也循环出来。
做个测试:
当在obj的原型里添加了属性,克隆出来的对象会有原型里的属性,而这个我们是不需要的,此时有两个方法可以解决:
1、Object.keys() 此方法会获取本对象里的所有key值,然后使用forEach来遍历。
2、obj.hasOwnProperty(key) 此方法判断指定key是否在指定对象里。(不会遍历原型)
2.2、完成封装
/* 1 */
function clone(obj) {
var objClone = Array.isArray(obj) ? [] : {};
if (obj instanceof Object && typeof obj !== 'function') {
for (var k in obj) {
if(obj.hasOwnProperty(k)) {
objClone[k] = clone(obj[k]);
}
}
} else {
objClone = obj;
}
return objClone;
}
/* 2 */
function clone(obj) {
var objClone = Array.isArray(obj) ? [] : {};
if (obj instanceof Object && typeof obj !== 'function') {
Object.keys(obj).forEach(function (k){
objClone[k] = clone(obj[k]);
});
} else {
objClone = obj;
}
return objClone;
}
对象的克隆大致完成了。此时克隆的对象本身不会再出现原型中的属性了,但是有时候我们又需要克隆出的对象的原型指向原对象的原型。
如果是直接创建的对象,它的原型为Object,此时克隆出的对象原型默认也是Object,这样就不用任何操作。
但当要克隆的对象是通过new操作符生成的,那么原对象原型上的方法,在新对象上就没办法用了。
解决方法:
__proto__ 属性,此属性就是原型链的链,通过指定生成函数的原型,实现原型的指向。
cloneObj.__proto__ = obj.__proto__;
解决原型的指向。