JSON.parse(JSON.stringify(obj))我们一般用来深拷贝,其过程说白了 就是利用JSON.stringify 将js对象序列化(JSON字符串),再使用JSON.parse来反序列化(还原)js对象;我们在使用 JSON.parse(JSON.stringify(xxx))时应该注意一下几点:
则JSON.stringify后再JSON.parse的结果,时间将只是字符串的形式。而不是时间对象;
var test = {
name: 'a',
date: [new Date(1536627600000), new Date(1540047600000)],
};
let b;
b = JSON.parse(JSON.stringify(test))
,则序列化的结果将只得到空对象;
const test = {
name: 'a',
date: new RegExp('\\w+'),
};
// debugger
const copyed = JSON.parse(JSON.stringify(test));
test.name = 'test'
console.error('ddd', test, copyed)
const test = {
name: 'a',
date: function hehe() {
console.log('fff')
},
};
// debugger
const copyed = JSON.parse(JSON.stringify(test));
test.name = 'test'
console.error('ddd', test, copyed)
const test = {
name: NaN,
date: function hehe() {
console.log('fff')
},
};
// debugger
const copyed = JSON.parse(JSON.stringify(test));
console.log( test, copyed)
,例如 如果obj中的对象是有构造函数生成的, 则使用JSON.parse(JSON.stringify(obj))深拷贝后,会丢弃对象的constructor;
function Person(name) {
this.name = name;
console.log(name)
}
const liai = new Person('liai');
const test = {
name: 'a',
date: liai,
};
// debugger
const copyed = JSON.parse(JSON.stringify(test));
test.name = 'test'
console.error('ddd', test, copyed)
第一个date的构造函数是Person 但是经过序列化再格式化已经失去了原有的构造函数 而是变成了Object
接下来这个方法比较通用可以参考
var clone = function (obj) {
if(obj === null) return null
if(typeof obj !== 'object') return obj;
if(obj.constructor===Date) return new Date(obj);
var newObj = new obj.constructor (); //保持继承链
for (var key in obj) {
if (obj.hasOwnProperty(key)) {
//不遍历其原型链上的属性
var val = obj[key];
newObj[key] = typeof val === 'object' ? arguments.callee(val) : val; // 使用arguments.callee解除与函数名的耦合
}
}
return newObj;
};
1、用new obj.constructor ()构造函数新建一个空的对象,而不是使用{}或者[],这样可以保持原形链的继承; 这样克隆出来的新对象也可以通过原型链访问构造函数的方法
2、用obj.hasOwnProperty(key)来判断属性是否来自原型链上,因为for…in…也会遍历其原型链上的可枚举属性。
3、上面的函数用到递归算法,在函数有名字,而且名字以后也不会变的情况下,这样定义没有问题。但问题是这个函数的执行与函数名 clone紧紧耦合在了一起。为了消除这种紧密耦合的现象,需要使用 arguments.callee。在严格模式下 arguments.callee 被禁用了,可以查看我的其他文章。