JavaScript里的对象复制方法大揭秘
最编程
2024-02-15 08:58:50
...
Object是引用类型,对引用类型的赋值只是赋的内存地址。
var foo = {
a: "abc"
}
console.log(foo.a); // abc
var bar = foo;
console.log(bar.a); // abc
foo.a = "yo foo";
console.log(foo.a); // yo foo
console.log(bar.a); // yo foo
bar.a = "whatup bar?";
console.log(foo.a); // whatup bar?
console.log(bar.a); // whatup bar?
foo和bar实际上是同一个对象的引用,bar并不能算作对foo的复制。
复制对象有深拷贝和浅拷贝之分。
浅拷贝
如果对象比较简单、只具有值类型的属性,可以使用扩展运算符或 Object.assign()
var obj = { foo: "foo", bar: "bar" };
var copy = { ...obj }; // Object {foo: "foo", bar: "bar"}
var obj = { foo: "foo", bar: "bar" };
var copy = Object.assign({}, obj); // Object {foo: "foo", bar: "bar"}
以上两种方法还可以用来合并对象。
var obj1 = { foo: "foo" };
var obj2 = { bar: "bar" };
var copySpread = { ...obj1, ...obj2 }; // Object {foo: "foo", bar: "bar"}
var copyAssign = Object.assign({}, obj1, obj2); // Object {foo: "foo", bar: "bar"}
深拷贝
针对深拷贝,需要使用其他办法,因为浅拷贝复制的是属性值。继承属性和不可枚举属性是不能拷贝的。假如源对象的属性值是一个对象的引用,那么它也只指向那个引用。
//浅拷贝
let obj1 = { a: 0 , b: { c: 0}};
let obj2 = Object.assign({}, obj1);
obj2.b.c = 3;
console.log(JSON.stringify(obj1)); // { a: 1, b: { c: 3}}
console.log(JSON.stringify(obj2)); // { a: 2, b: { c: 3}}
//深拷贝
obj1 = { a: 0 , b: { c: 0}};
let obj3 = JSON.parse(JSON.stringify(obj1));
obj1.a = 4;
obj1.b.c = 4;
console.log(JSON.stringify(obj3)); // { a: 0, b: { c: 0}}
然而这种方法也有其局限,此方法仅在原对象包含可序列化值类型且没有任何循环引用时才有效。
对于复杂对象的深拷贝,MDN提供了一种方法
const obj = {
foo: 1,
get bar() {
return 2;
}
};
let copy = Object.assign({}, obj);
console.log(copy); // { foo: 1, bar: 2 } copy.bar的值来自obj.bar的getter函数的返回值
// 下面这个函数会拷贝所有自有属性的属性描述符
function completeAssign(target, ...sources) {
sources.forEach(source => {
let descriptors = Object.keys(source).reduce((descriptors, key) => {
descriptors[key] = Object.getOwnPropertyDescriptor(source, key);
return descriptors;
}, {});
// Object.assign 默认也会拷贝可枚举的Symbols
Object.getOwnPropertySymbols(source).forEach(sym => {
let descriptor = Object.getOwnPropertyDescriptor(source, sym);
if (descriptor.enumerable) {
descriptors[sym] = descriptor;
}
});
Object.defineProperties(target, descriptors);
});
return target;
}
copy = completeAssign({}, obj);
console.log(copy);
// { foo:1, get bar() { return 2 } }