【问题标题】:After I do "x=y" and change "x", it also changes "y". How do I prevent this?在我执行“x=y”并更改“x”之后,它也会更改“y”。我该如何防止这种情况?
【发布时间】:2021-03-13 11:52:16
【问题描述】:

例如我有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; 会有所帮助。但由于某种原因,它也会更改定义为常量的值。

【问题讨论】:

  • “我讨厌 JavaScript。” Javascript 当然有它的问题,但很少有语言具有默认不可变的集合。 Python、Java、Ruby、C++ 等都对集合和对象使用引用类型,并且您将遇到相同的“问题”。这只是编程的一部分。
  • 另外,根据您对使用 const 的编辑:const 仅防止重新绑定名称,const 引用仍然是可变的。 const foo = {}; foo.a = 1;

标签: javascript arrays variable-assignment


【解决方案1】:
var unchangedListings = listings;

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

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

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

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

【讨论】:

  • “浅拷贝”是什么意思? @JaredSmith
  • 以这种方式使用传播不会深度克隆:`const foo = [{}, {}]; const bar = [...foo]; foo[0].a = 1;控制台.log(bar[0].a);将打印 1。我在示例中使用了数组,但对象扩展也是如此:foo 和 bar 的第零位的对象是同一个对象,它没有被克隆。
  • 明白了,我更新了答案,@JaredSmith
  • +1 基于您的编辑的另一个挑剔:仅适用于 JSON 可序列化的内容。 Javascript 中有很多东西不是(例如函数)。
【解决方案2】:

如果列表或字典是嵌套的,您可以使用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,他们很好地解释了浅拷贝和深拷贝之间的区别。

【讨论】:

  • const currentLowestSeller = JSON.parse(JSON.stringify(listings[0]));好吗??
  • @narra_kk 是的,它更好,你提到的那个方法只适用于 JSON 可序列化的东西。 Javascript 中有很多东西不是(例如函数)。
【解决方案3】:

问题的根源

您看到的行为对于大多数语言来说都是常见的,与 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 内置函数很快,但如果您的对象包含 @ 987654324@ 或 Date 实例)。

查看此线程以获取更多信息: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;

【讨论】:

    【解决方案4】:

    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
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2020-07-17
      • 2012-02-21
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多