为什么在JavaScript中更改数组会影响数组的副本?


80

我编写了以下JavaScript:

var myArray = ['a', 'b', 'c'];
var copyOfMyArray = myArray;
copyOfMyArray.splice(0, 1);
alert(myArray); // alerts ['b','c']
alert(copyOfMyArray); // alerts ['b','c']

var myNumber = 5;
var copyOfMyNumber = myNumber;
copyOfMyNumber = copyOfMyNumber - 1;
alert(myNumber); // alerts 5
alert(copyOfMyNumber); // alerts 4        

此代码声明一个变量myArray并将其设置为数组值。然后,它声明第二个变量copyOfMyArray并将其设置为myArray。它对进行操作copyOfMyArray,然后向myArray和发出警报copyOfMyArray。不知何故,当我对执行操作时copyOfMyArray,似乎对执行了相同的操作myArray

然后,代码使用数字值执行相同的操作:它声明一个变量myNumber并将其设置为数字值。然后,它声明第二个变量copyOfMyNumber并将其设置为myNumber。它对进行操作copyOfMyNumber,然后向myNumber和发出警报copyOfMyNumber。在这里,我得到预期的行为:对不同的价值观myNumbercopyOfMyNumber

数组与JavaScript中的数字有什么区别,似乎更改数组会更改该数组副本的值,而更改数字不会更改该数字副本的值吗?

我猜测由于某种原因,数组是通过引用引用的,而数字是通过值引用的,但是为什么呢?我如何知道其他对象会有什么行为?

Answers:


107

JavaScript中的数组也是一个对象,变量仅包含对象的引用,而不是对象本身。因此,两个变量都引用相同的对象。

您与数字示例的比较是不正确的。您将新值分配给copyOfMyNumber。如果您为其分配新值,copyOfMyArray则也不会更改myArray

您可以使用slice [docs]创建数组的副本:

var copyOfMyArray = myArray.slice(0);

但是请注意,这只会返回浅表副本,即数组中的对象将不会被克隆。


+1-出于好奇,myArray.slice(0);直接在该上下文中分配是否有任何弊端?
jAndy 2011年

2
@Rice:不,我只是进行澄清。如果要深层复制,则必须自己编写一些内容。但是我敢肯定,您会找到执行此操作的脚本。
菲利克斯·克林

@FelixKling:我没有一个例子。我之所以问是因为您首先应用了原型方法。
jAndy 2011年

@jAndy:嗯,所以您提到的是...我只是有点困惑,最近我只是更反过来;)
Felix Kling

23

好吧,唯一可能的答案(也是正确的答案)是您实际上并没有在复制阵列。当你写

var copyOfArray = array;

您正在将对同一数组的引用分配到另一个变量中。换句话说,它们都指向同一个对象。


我想说的是您没有完全分配参考指针,而是像参考副本一样进行分配。由于如果将obj传递给函数并尝试用函数内的另一个新对象替换它,则不会更改原始对象。
kashesandr

1
@kashesandr是的,“分配参考”是指“分配参考副本”,这是事实。但是,两个相等的引用始终相等,就像该数字的两个实例5始终相等一样。
尖尖的

15

因此,这里的每个人都很好地解释了为什么会发生这种情况-我只是想删除一行,然后让您知道我如何解决此问题-相当容易:

thingArray = ['first_thing', 'second_thing', 'third_thing']
function removeFirstThingAndPreserveArray(){
  var copyOfThingArray = [...thingArray]
  copyOfThingArray.shift();
  return copyOfThingArray;
}

这是使用...传播语法。

传播语法源

编辑:至于为什么这样,并回答您的问题:

数组与JavaScript中的数字有什么区别,似乎更改数组会更改该数组副本的值,而更改数字不会更改该数字副本的值吗?

答案是,在JavaScript中,数组和对象是可变的,而字符串和数字以及其他基元是不可变的。当我们做一个像这样的作业时:

var myArray = ['a', 'b', 'c']; var copyOfMyArray = myArray;

copyOfMyArray实际上只是对myArray的引用,而不是实际的副本。

我会推荐这篇文章,什么是不变和可变的数据结构?,以深入研究该主题。

MDN词汇表:可变


2
正是我想要的。
Fabien TheSolution

6

克隆对象-

loop / array.push产生类似结果array.slice(0)array.clone()。值都是通过引用传递的,但是由于大多数原始数据类型都是不可变的,因此后续操作会产生所需的结果-“克隆”。当然,对于允许修改原始引用(它们是可变类型)的对象和数组,情况并非如此。

请看以下示例:

const originalArray = [1, 'a', false, {foor: 'bar'}]
const newArray = [];

originalArray.forEach((v, i) => {
    newArray.push(originalArray[i]);
});

newArray[0] = newArray[0] + 1;
newArray[1] = 'b';
newArray[2] = true;
newArray[3] = Object.assign(newArray[3], {bar: 'foo'});

在newArray索引上运行的操作均会产生所需的结果,但最后一个(对象)除外,因为最终对象(通过引用复制)也将使originalArray [3]发生突变。

https://jsfiddle.net/7ajz2m6w/

请注意,它也array.slice(0) and array.clone()受到相同的限制。

解决此问题的一种方法是在推送序列期间有效地克隆对象:

originalArray.forEach((v, i) => {
    const val = (typeof v === 'object') ? Object.assign({}, v) : v;
    newArray.push(val);
});

https://jsfiddle.net/e5hmnjp0/

干杯


3

在JS中,运算符“ =”将指针复制到数组的存储区。如果要将数组复制到另一个数组,则必须使用克隆功能。

因为整数是原始类型,所以整数是不同的。

S.


1

除原始数据类型(字符串和数字IIRC)外,所有内容均按引用复制。


那不是真的 所有分配都分配引用。字符串和数字是不可变的。
Slaks 2011年

1

您没有任何副本。
您有多个变量持有相同的数组。

同样,您有多个拥有相同编号的变量。

当您编写时copyOfMyNumber = ...,您要在变量中添加一个新数字。
这就像写作copyOfMyArray = ...

在编写时copyOfMyArray.splice,您正在修改原始数组
用数字是不可能的,因为数字是不可变的,无法修改,


1

在arrayCopy中创建原始数组的过滤器。这样对新数组的更改不会影响原始数组。

var myArray = ['a', 'b', 'c'];
var arrayCopy = myArray.filter(function(f){return f;})
arrayCopy.splice(0, 1);
alert(myArray); // alerts ['a','b','c']
alert(arrayCopy); // alerts ['b','c']

希望能帮助到你。


1

浅表复制的问题是不是所有对象都被克隆,而是被引用了,因此array.slice(0)仅适用于文字数组,而不能使用对象数组进行浅表复制。在这种情况下,一种方法是..

var firstArray = [{name: 'foo', id: 121}, {name: 'zoo', id: 321}];
var clonedArray = firstArray.map((_arrayElement) => Object.assign({}, _arrayElement));  
console.log(clonedArray);
// [{name: 'foo', id: 121}, {name: 'zoo', id: 321}]  // shallow copy

0

您可以根据情况添加一些错误处理,并使用类似于以下功能的方法解决问题。请评论任何错误/问题/效率的想法。

function CopyAnArray (ari1) {
   var mxx4 = [];
   for (var i=0;i<ari1.length;i++) {
      var nads2 = [];
      for (var j=0;j<ari1[0].length;j++) {
         nads2.push(ari1[i][j]);
      }
      mxx4.push(nads2);
   }
   return mxx4;
}

-1

除非您克隆或复制,否则数组或javascript中的对象始终具有相同的引用。这是一个例子:

http://plnkr.co/edit/Bqvsiddke27w9nLwYhcl?p=preview

// for showing that objects in javascript shares the same reference

var obj = {
  "name": "a"
}

var arr = [];

//we push the same object
arr.push(obj);
arr.push(obj);

//if we change the value for one object
arr[0].name = "b";

//the other object also changes
alert(arr[1].name);

对于对象克隆,我们可以在jquery和angular.copy()中使用.clone(),这些函数将创建具有其他引用的新对象。如果您知道执行此操作的更多功能,请告诉我,谢谢!

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.