JS: Object.assign() 创建深拷贝还是浅拷贝
JS: Does Object.assign() create deep copy or shallow copy
我刚刚遇到
这个概念
var copy = Object.assign({}, originalObject);
它在“copy
”对象中创建原始对象的副本。但是,我的问题是,这种克隆对象的方式创建的是深拷贝还是浅拷贝?
PS: 令人困惑的是,如果它创建一个深拷贝,那么这将是克隆对象的最简单方法。
忘掉深拷贝,即使是浅拷贝也不安全,如果你复制的对象有一个 属性 并且 enumerable
属性设置为 false。
MDN:
The Object.assign() method only copies enumerable and own properties
from a source object to a target object
举个例子
var o = {};
Object.defineProperty(o,'x',{enumerable: false,value : 15});
var ob={};
Object.assign(ob,o);
console.log(o.x); // 15
console.log(ob.x); // undefined
通过使用Object.assign()
,您实际上是在对您的对象进行浅拷贝。每当我们进行诸如将一个对象分配给另一个对象的操作时,我们实际上执行了一个浅拷贝,即如果 OBJ1 是一个对象,通过另一个对象 OBJ2 修改它也会反映 OBJ1 的变化。
创建浅拷贝
For deep cloning, we need to use other alternatives because
Object.assign() copies property values. If the source value is a
reference to an object, it only copies that reference value.
对于 redux 而言,Object.assign()
就足够了,因为 redux 应用程序的状态仅包含不可变值 (JSON)。
对于小型 Data structures
我看到 JSON.stringify()
和 JSON.parse()
工作得很好。
// store as JSON
var copyOfWindowLocation = JSON.stringify(window.location)
console.log("JSON structure - copy:", copyOfWindowLocation)
// convert back to Javascript Object
copyOfWindowLocation = JSON.parse(copyOfWindowLocation)
console.log("Javascript structure - copy:", copyOfWindowLocation)
var copy = Object.assign({}, originalObject);
正在更改副本的浅拷贝是否也反映了原始对象中的更改。所以要执行深层复制,我会推荐 lodash
cloneDeep
import cloneDeep from 'lodash/cloneDeep';
var copy = cloneDeep(originalObject);
如上所述,Object.assign()
会进行浅克隆,无法复制源对象的自定义方法,无法复制带有enumerable: false
的属性。
保留方法和不可枚举的属性需要更多代码,但不会更多。
这将对数组或对象进行浅表克隆,复制源的方法和所有属性:
function shallowClone(src) {
let dest = (src instanceof Array) ? [] : {};
// duplicate prototypes of the source
Object.setPrototypeOf(dest, Object.getPrototypeOf(src));
Object.getOwnPropertyNames(src).forEach(name => {
const descriptor = Object.getOwnPropertyDescriptor(src, name);
Object.defineProperty(dest, name, descriptor);
});
return dest;
}
示例:
class Custom extends Object {
myCustom() {}
}
const source = new Custom();
source.foo = "this is foo";
Object.defineProperty(source, "nonEnum", {
value: "do not enumerate",
enumerable: false
});
Object.defineProperty(source, "nonWrite", {
value: "do not write",
writable: false
});
Object.defineProperty(source, "nonConfig", {
value: "do not config",
configurable: false
});
let clone = shallowClone(source);
console.log("source.nonEnum:",source.nonEnum);
// source.nonEnum: "do not enumerate"
console.log("clone.nonEnum:", clone.nonEnum);
// clone.nonEnum: – "do not enumerate"
console.log("typeof source.myCustom:", typeof source.myCustom);
// typeof source.myCustom: – "function"
console.log("typeof clone.myCustom:", typeof clone.myCustom);
// typeof clone.myCustom: – "function"
Object.assign 仅创建浅拷贝。
const originalObject = {
api : 'POST',
contentType : 'JSON',
userData : {
name : 'Triver',
email : 'postaction@yahoo.com'
},
responseTime: '10ms'
}
const originalObjectRef = Object.assign({}, originalObject);
originalObjectRef.contentType = 'XHTML';
originalObjectRef.userData.name = 'Red John';
console.log(originalObject);
Output:
{
"api": "POST",
"contentType": "JSON",
"userData": {
"name": "Red John",
"email": "postaction@yahoo.com"
},
"responseTime": "10ms"
}
在浅拷贝中,引用变量主要存放的是它引用的对象的地址。
当一个新的引用变量被赋予旧引用变量的值时,存储在旧引用变量中的地址被复制到新变量中。
这意味着新旧引用变量都指向内存中的同一个对象。
因此,如果对象的状态通过任何引用变量发生变化,那么这两个变量都会反映出来。
注:下面是浅拷贝的ES6方式
const originalObjectRef = {...originalObject};
希望这对某人有所帮助,谢谢。
我刚刚遇到
这个概念var copy = Object.assign({}, originalObject);
它在“copy
”对象中创建原始对象的副本。但是,我的问题是,这种克隆对象的方式创建的是深拷贝还是浅拷贝?
PS: 令人困惑的是,如果它创建一个深拷贝,那么这将是克隆对象的最简单方法。
忘掉深拷贝,即使是浅拷贝也不安全,如果你复制的对象有一个 属性 并且 enumerable
属性设置为 false。
MDN:
The Object.assign() method only copies enumerable and own properties from a source object to a target object
举个例子
var o = {};
Object.defineProperty(o,'x',{enumerable: false,value : 15});
var ob={};
Object.assign(ob,o);
console.log(o.x); // 15
console.log(ob.x); // undefined
通过使用Object.assign()
,您实际上是在对您的对象进行浅拷贝。每当我们进行诸如将一个对象分配给另一个对象的操作时,我们实际上执行了一个浅拷贝,即如果 OBJ1 是一个对象,通过另一个对象 OBJ2 修改它也会反映 OBJ1 的变化。
For deep cloning, we need to use other alternatives because Object.assign() copies property values. If the source value is a reference to an object, it only copies that reference value.
对于 redux 而言,Object.assign()
就足够了,因为 redux 应用程序的状态仅包含不可变值 (JSON)。
对于小型 Data structures
我看到 JSON.stringify()
和 JSON.parse()
工作得很好。
// store as JSON
var copyOfWindowLocation = JSON.stringify(window.location)
console.log("JSON structure - copy:", copyOfWindowLocation)
// convert back to Javascript Object
copyOfWindowLocation = JSON.parse(copyOfWindowLocation)
console.log("Javascript structure - copy:", copyOfWindowLocation)
var copy = Object.assign({}, originalObject);
正在更改副本的浅拷贝是否也反映了原始对象中的更改。所以要执行深层复制,我会推荐 lodash
cloneDeep
import cloneDeep from 'lodash/cloneDeep';
var copy = cloneDeep(originalObject);
如上所述,Object.assign()
会进行浅克隆,无法复制源对象的自定义方法,无法复制带有enumerable: false
的属性。
保留方法和不可枚举的属性需要更多代码,但不会更多。
这将对数组或对象进行浅表克隆,复制源的方法和所有属性:
function shallowClone(src) {
let dest = (src instanceof Array) ? [] : {};
// duplicate prototypes of the source
Object.setPrototypeOf(dest, Object.getPrototypeOf(src));
Object.getOwnPropertyNames(src).forEach(name => {
const descriptor = Object.getOwnPropertyDescriptor(src, name);
Object.defineProperty(dest, name, descriptor);
});
return dest;
}
示例:
class Custom extends Object {
myCustom() {}
}
const source = new Custom();
source.foo = "this is foo";
Object.defineProperty(source, "nonEnum", {
value: "do not enumerate",
enumerable: false
});
Object.defineProperty(source, "nonWrite", {
value: "do not write",
writable: false
});
Object.defineProperty(source, "nonConfig", {
value: "do not config",
configurable: false
});
let clone = shallowClone(source);
console.log("source.nonEnum:",source.nonEnum);
// source.nonEnum: "do not enumerate"
console.log("clone.nonEnum:", clone.nonEnum);
// clone.nonEnum: – "do not enumerate"
console.log("typeof source.myCustom:", typeof source.myCustom);
// typeof source.myCustom: – "function"
console.log("typeof clone.myCustom:", typeof clone.myCustom);
// typeof clone.myCustom: – "function"
Object.assign 仅创建浅拷贝。
const originalObject = {
api : 'POST',
contentType : 'JSON',
userData : {
name : 'Triver',
email : 'postaction@yahoo.com'
},
responseTime: '10ms'
}
const originalObjectRef = Object.assign({}, originalObject);
originalObjectRef.contentType = 'XHTML';
originalObjectRef.userData.name = 'Red John';
console.log(originalObject);
Output:
{
"api": "POST",
"contentType": "JSON",
"userData": {
"name": "Red John",
"email": "postaction@yahoo.com"
},
"responseTime": "10ms"
}
在浅拷贝中,引用变量主要存放的是它引用的对象的地址。 当一个新的引用变量被赋予旧引用变量的值时,存储在旧引用变量中的地址被复制到新变量中。 这意味着新旧引用变量都指向内存中的同一个对象。 因此,如果对象的状态通过任何引用变量发生变化,那么这两个变量都会反映出来。
注:下面是浅拷贝的ES6方式
const originalObjectRef = {...originalObject};
希望这对某人有所帮助,谢谢。