由 Object.Assign() 函数生成的复制对象具有副作用

Copied object generated by Object.Assign() function has side effect?

本文关键字:复制 对象 副作用 Assign Object 函数      更新时间:2023-09-26

我有一个名为result的对象,它由两个对象组成,如下所示:

const a = {bar: {baz: 2}};
var b = {foo: 1};
var result = Object.assign({}, a, b);
console.log(result, a, b);
// result -> {bar: {baz: 2}, foo: 1}
// a -> {bar: {baz: 2}}
// b -> {foo: 1}

现在,我正在更改结果对象的 bar 属性,例如:

result.bar.baz = 3;
result.foo = 4;
console.log(result, a, b);
// result -> {bar: {baz: 3}, foo: 4}
// a -> {bar: {baz: 3}} intresting part
// b -> {foo: 1} intresting, too!

(您可以将代码复制并粘贴到javascript控制台,以便顺便查看两种情况的结果)

这里有两件奇怪的事情。第一个是我正在更改结果对象的属性,但常量对象 a 的属性也会更改。即使第一个是 Object.assign 函数的情况,我该如何更改常量变量?假设尽管常量变量突变,情况确实如此,那么为什么属性foo的变化没有反映到对象b上呢?我这样做是因为我通常使用 Object.assign 来复制对象,但我想这是该函数非常奇怪的问题。对这个案子有什么想法吗?谢谢。

使用 const 声明变量只会防止它被更改为另一个值。它不会阻止该值引用的数据发生更改。

const foo = {prop: 'bar'};
foo.prop = 'baz'; // Works well
foo = 'baz'; // TypeError: invalid assignment to const `foo'

如果要阻止对象被更改,可以将其冻结。

Object.freeze(foo);
foo.prop = 'buz'; // TypeError: "prop" is read-only

但是,这只会影响自己的财产。如果其中一个对象的值是另一个对象,则它不会被冻结。

Object.assign也是如此。它只复制自己的属性,如果它们的值是一个对象,它不会"克隆"它。也就是说,引用仍将相同,并且将反映更改。

如果要深度克隆对象,请参阅克隆对象的最有效方法是什么?

Object.assign 将起作用,但添加了陷阱,即如果您要从中分配的任何属性包含对象作为值,它不会创建该对象的副本,因此引用不会更改,创建对象中的属性将指向相同的嵌套对象。

JavaScript 中的常量也可能具有欺骗性,只要您不尝试将其重新分配给新对象或其他原语,您就可以在"const"对象中添加和删除属性。

数组

也会发生同样的情况,您可以创建一个"const"数组,但将其推开。

https://jsfiddle.net/eu9yg37s/6/只是我为了展示我的意思而胡闹的东西。

const a = {bar: {baz: 2}};
var b = {foo: 1};
// Example of customizer function that may take into account nested objects and properly copy them using lodash 4.x
var customizer = function(objValue, srcValue) {
  if (typeof srcValue === 'object' && typeof srcValue !== null) {
    return _.assignWith({}, srcValue, customizer);
  }
  return _.isUndefined(objValue) ? srcValue : objValue;
}
// This calls assign, but will invoke the customizer function
var result = _.assignWith({}, a, b, customizer);
result.bar.baz = 3;
result.foo = 4;
console.log(result, a, b);
// 'Constant' Array Example
const hi = [true, false, 'hi'];
hi[2] = 23;
console.log('hi.pop', hi.pop());
console.log('hi', hi);
// These will error, uncomment out to see it errors on any of these attempts
//hi = 3;
//hi = 'no';
hi = [true, false, 23];
//hi = false;
//hi = {};

更改不会反映在 b 中,因为它在分配操作期间不是嵌套对象,因此我们创建的对象中的属性 foo 指向新的基元 1