我做了"x=y",改了"x"之后,它也改了"y"。我该如何防止这种情况?

After I do "x=y" and change "x", it also changes "y". How do I prevent this?

例如我有 listings 数组:

[ { name: 'bob', price: '10' }, { name: 'jack', price: '12' } ]

我正在尝试找到最低价的卖家并稍后使用它的数据。 我做 var currentLowestSeller = listings[0];

现在 currentLowestSeller 是:

{ name: 'bob', price: '10' }

稍后我做的更改是 currentLowestSeller 但我不想更改主 listings 数组。 我做 currentLowestSeller.price = currentLowestSeller.price * 0.5; 并且在此列表数组之后如下所示:

[ { name: 'bob', price: 5 }, { name: 'jack', price: '12' } ]

如何防止这种情况发生?

如果你想重新创建这个 运行 这个代码:

var listings = [];

var name1 = 'bob';
var name2 = 'jack';
var price1 = '10';
var price2 = '12';

listings.push({
  name: name1,
  price: price1
})

listings.push({
  name: name2,
  price: price2
})

var currentLowestSeller = listings[0];
currentLowestSeller.price = currentLowestSeller.price * 0.5;

console.log(listings);

我试过的:

我尝试在执行任何操作之前创建 listings 数组的副本。

var unchangedListings = listings;
var currentLowestSeller = listings[0];
currentLowestSeller.price = currentLowestSeller.price * 0.5;

console.log(unchangedListings);

但是没有用。

后来我决定 const unchangedListings = listings; 会有帮助。但出于某种原因,它还会更改定义为常量的值。

var unchangedListings = listings;

这意味着,unchangedListings 指示 listings 的值,因此如果您更改 unchangedListings 值,则意味着您也在更新 listings.

为防止这种情况,您需要克隆该值。您应该深度克隆该对象。

var currentLowestSeller = JSON.parse(JSON.stringify(listings[0]))

var currentLowestSeller = Object.assign({}, listings[0])

如果列表或字典是嵌套的,您可以使用 Ramda library:

中的 clone
import { clone } from 'ramda';

var currentLowestSeller = clone(listings[0]);

您可以在这里找到更多信息:https://medium.com/javascript-in-plain-english/how-to-deep-copy-objects-and-arrays-in-javascript-7c911359b089,它们很好地解释了浅拷贝和深拷贝之间的区别。

问题的根源

您看到的行为对大多数语言来说都是常见的,与 javascript 无关。

数组仅包含 对它们包含的对象的引用。从数组(或与此相关的对象)中提取键不会复制键的值。如果是这种情况,将无法对程序的状态进行任何更改。

var a = { toto: 1 };  // create object
var b = a;            // b is pointing the the same object
b['toto'] = 2;        // update the object (there is only one)

console.log(a == b);  // true because a and b are the SAME object (not just equal,
                      // both a and b point to the same place in the computer memory)

console.log(a);       // { toto: 2 } both objects have been edited

如果您需要操作一个对象,而不修改原始对象,您需要显式制作一个副本。

但是,当使用嵌套对象或嵌套数组时,就会出现问题。您需要“深拷贝”还是“浅拷贝”?

浅拷贝

浅拷贝意味着只复制“第一层”。

var a = { toto: 1, tata: { tutu: 1 } };
var b = { ... a }; // make a "shallow copy"

// We change "b", did "a" change? => No
b.toto = 2;
console.log(a); // { toto: 1, tata: { tutu: 1 } }
                // "a" was not modified!

console.log(b); // { toto: 2, tata: { tutu: 1 } }
                // "b" was modified!

// we change a nested object in "b", did "a" change? => Yes
b.tata.tutu = 2;
console.log(a); // { toto: 1, tata: { tutu: 2 } }
                // "a" was modified!

console.log(b); // { toto: 2, tata: { tutu: 2 } }
                // "b" was modified!

深拷贝

深拷贝会复制所有嵌套的数组和对象(并且会带来显着的性能成本)。

Javascript 没有内置语言来执行深拷贝,因为它不是常见的操作,而且很昂贵。

执行对象深度复制的最常见方法是使用 JSON 内置函数,但是存在许多不同优缺点的方法(例如,使用 JSON 内置函数是很快,但如果您的对象包含 NaNDate 个实例,则会中断。

查看此线程以获取更多信息:What is the most efficient way to deep clone an object in JavaScript?

var a = { toto: 1, tata: { tutu: 1 } };
var b = JSON.parse(JSON.stringify(a)); // make a "deep copy"

// a and b are now completely different, they share nothing in memory
// we can edit any subobject, they will not be any consequence between them.
a.tata.tutu = 2;

Javascript(以及许多其他语言)最重要的概念之一是引用类型的概念。 Javascript 有 3 种通过引用传递的数据类型:ArrayFunctionObject。由于理解这一点非常重要,因此我建议阅读此 article.

你的情况:

var unchangedListings = listings; // still points to listings
var currentLowestSeller = listings[0]; // changes listings

在改变数组之前复制数组始终是一个好习惯:

const currentLowestSeller = [... listings]; // currentLowestSeller points to a new array