这句代码是怎么实现深拷贝的?
请教个问题:
这句代码:var newObject = JSON.parse(JSON.stringify(oldObject));
可以简单的实现深复制,但是有几个点我不明白,
首先:JSON.stringify() 方法可以将任意的 JavaScript 值序列化成 JSON 字符串,
也就是可以把传入的数组,object对象等序列化成JSON字符串,
然后:JSON.parse() 方法可以将一个 JSON 字符串解析成为一个 JavaScript 值。
在解析过程中还可以选择性的篡改某些属性的原始解析值,也就是把刚序列化成JSON字符串的又在解析成一次并赋给newObject
不明白的是这其中的过程是怎么实现深复制了?
我了解的浅复制是两个对象都指向同一块内存地址,当修改a时,b也会自动发生变化。
而深复制是重新开辟出一块内存地址,在实现深复制后,会把a对象的各个属性全部复制到新的内存地址中,对于引用类型的变量,需要用递归来使对象的所有属性都被复制到,此后a与b没有关联了,修改a但是b不会改变
可是用JSON.parse(JSON.stringify(oldObject))
这个方法实现深复制的过程还是看不懂,麻烦解释下可以吗?
下面是测试的代码:
function cloneObject(src) {
var clone = JSON.parse(JSON.stringify(src));
return clone;
}
// 测试用例:
var srcObj = {
a: 1,
b: {
b1: ["hello", "hi"],
b2: "JavaScript"
}
};
var abObj = srcObj;
var tarObj = cloneObject(srcObj);
srcObj.a = 2;
srcObj.b.b1[0] = "Hello";
//浅复制
console.log(abObj.a); //2
console.log(abObj.b.b1[0]); //hello
//深复制
console.log(tarObj.a); // 1
console.log(tarObj.b.b1[0]); // "hello"
console.log(tarObj.b.b1[1]); //'hi'
console.log( typeof(tarObj.a) ); //Number
另外,对这句代码SF上的评论:
上面这种方法好处是非常简单易用,但是坏处也显而易见,这会抛弃对象的constructor,也就是深复制之后,无论这个对象原本的构造函数是什么,在深复制之后都会变成Object。另外诸如RegExp对象是无法通过这种方式深复制的。@jerryzou
对显而易见的坏处,还是看不明白 =_=!! 可以详细解释下吗?
然后,在GitHub上看到的实现深复制的代码:
function cloneObject(src) {
var clone = src;
// 对于Date,String,Boolean等引用类型的数据,需要考虑调用构造函数重新构造,
//直接赋值依然会有引用问题(不是真正的clone引用变量)
// 对于 Date
if (src instanceof Date) {
clone = new Date(src.getDate());
return clone;
}
// 对于Object和Array的遍历,可以使用for in,
//这样可以保证在在Array对象上扩展的属性也可以正确复制
// 对于 数组
if (src instanceof Array) {
clone = [];
for (var key in src) {
clone[key] = cloneObject(src[key]);
}
return clone;
}
// 对于 Object
if (src instanceof Object) {
clone = {};
for (var key in src) {
if (src.hasOwnProperty(key)) { // 忽略掉继承属性
clone[key] = cloneObject(src[key]);
}
}
return clone;
}
// 对于 数字 字符串 布尔 null undefined
return src;
}
我个人觉得比较完善,不过对使用 hasOwnProperty() 方法,排除继承的属性。这是为什么?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
var newObject = JSON.parse(JSON.stringify(oldObject));
此时的obj_stringify和oldObject对象指向2个不同的地址
将字符串反序列化为一个对象,此时的newObject和oldObject指向2个不同的地址
就像
currentDate和copiedDate表示的同一个时间,但是是2个不同的对象
另外
使用JSON.stringify序列化一个对象时
序列化后的属性出现的顺序是不定的,除了数组中的元素-其按数组中位置顺序序列化
Boolean/Number/String对象转成其原始值
如果属性值为undefined、函数对象,symbol(ES6中新类型),那么这个属性要么在序列化的时候被忽略,要么被转成null(当在数组中出现时)
以symbol类型为属性key的属性被完全忽略
不可枚举的属性也被忽略
所以JSON.stringify并不能被用户来深度复制对象,这种情况只使用于属性值为基本类型或数组(元素项也为基本类型)的情形
JSON.parse(JSON.stringify(oldObject))
这样做 是把一个对象 变成json字符串格式 就像 {"xxx":"xcv"} 被变成了 '{"xxx":"xcv"}'
然后再把后者的字符串编译成一个object 因为他是一个符合json格式的字符串。
这样做通常是用来朝http 发请求的时候做为报文传递使用。
深度copy的时候 不适合的原因是 一个对象 上可能还有很多其他的无法被转为json string的东西
这一点你可以载入一个jquery用console.log 打印 $(document) 看看 里面有很多东西,但是他还是一个对象来的。你深度copy 它的时候 就不可以用前面的方法。
当然,简单规则的数据还是可以用前一种方法粗暴处理的,如果你和你的同伙儿都清楚自己在干什么的话。