### 关于JSON.parse(JSON.stringify(obj))实现深拷贝应该注意的坑
JSON.parse(JSON.stringify(obj))我们一般用来深拷贝,其过程说白了 就是利用JSON.stringify 将js对象序列化(JSON字符串),再使用JSON.parse来反序列化(还原)js对象;
在使用的时候要注意几点
1.如果obj里面有时间对象,则JSON.stringify后再JSON.parse的结果,时间将只是字符串的形式。而不是时间对象;
var test = { name: 'a', date: [new Date(1536627600000), new Date(1540047600000)], }; let b; b = JSON.parse(JSON.stringify(test))
console.log(b)
打印的结果是
{ name: 'a', date: [ '2018-09-11T01:00:00.000Z', '2018-10-20T15:00:00.000Z' ] }
2.如果obj里有RegExp、Error对象,则序列化的结果将只得到空对象;
const test = { name: 'cg', date: new RegExp('\w+'), }; const copyed = JSON.parse(JSON.stringify(test)); test.name = 'test' console.log('ddd', test, copyed)
打印的结果是
ddd { name: 'test', date: /w+/ } { name: 'cg', date: {} }
3.如果obj里有函数,undefined,则序列化的结果会把函数或 undefined丢失;
const test = { name: 'cg', date: function hehe() { console.log('fff') }, }; const copyed = JSON.parse(JSON.stringify(test)); console.log('ddd', test, copyed)
打印的结果是
ddd { name: 'cg', date: [Function: hehe] } { name: 'cg' }
4.如果obj里有NaN、Infinity和-Infinity,则序列化的结果会变成null
const test = { name: NaN, date: function hehe() { console.log('fff') }, }; const copyed = JSON.parse(JSON.stringify(test)); console.log('ddd', test, copyed)
打印的结果是
ddd { name: NaN, date: [Function: hehe] } { name: null }
5.JSON.stringify()只能序列化对象的可枚举的自有属性,例如 如果obj中的对象是有构造函数生成的, 则使用JSON.parse(JSON.stringify(obj))深拷贝后,会丢弃对象的constructor;
liai
ddd { name: 'cg', date: Person { name: 'liai' } } { name: 'cg', date: { name: 'liai' } }
6.如果对象中存在循环引用的情况也无法正确实现深拷贝;
以上,如果拷贝的对象不涉及上面讲的情况,可以使用JSON.parse(JSON.stringify(obj))实现深拷贝,但是涉及到上面的情况,可以考虑使用如下方法实现深拷贝:
function deepClone(data) { const type = this.judgeType(data); let obj; if (type === 'array') { obj = []; } else if (type === 'object') { obj = {}; } else { // 不再具有下一层次 return data; } if (type === 'array') { // eslint-disable-next-line for (let i = 0, len = data.length; i < len; i++) { obj.push(this.deepClone(data[i])); } } else if (type === 'object') { // 对原型上的方法也拷贝了.... // eslint-disable-next-line for (const key in data) { obj[key] = this.deepClone(data[key]); } } return obj; }, function judgeType(obj) { // tostring会返回对应不同的标签的构造函数 const toString = Object.prototype.toString; const map = { '[object Boolean]': 'boolean', '[object Number]': 'number', '[object String]': 'string', '[object Function]': 'function', '[object Array]': 'array', '[object Date]': 'date', '[object RegExp]': 'regExp', '[object Undefined]': 'undefined', '[object Null]': 'null', '[object Object]': 'object', }; if (obj instanceof Element) { return 'element'; } return map[toString.call(obj)]; },