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

时间:2011-07-07 14:38:11

标签: javascript

我写了以下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执行操作,然后向myArraycopyOfMyArray发出警报。不知何故,当我在copyOfMyArray上执行操作时,似乎在myArray上执行了相同的操作。

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

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

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

16 个答案:

答案 0 :(得分:90)

JavaScript中的数组也是对象,变量只将引用保存到对象,而不是对象本身。因此,两个变量都引用相同的对象。

您与数字示例的比较不正确顺便说一下。您为copyOfMyNumber分配了一个新值。如果您为copyOfMyArray分配新值,则不会更改myArray

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

var copyOfMyArray = myArray.slice(0);

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

答案 1 :(得分:20)

嗯,唯一可能的答案 - 也就是正确的答案 - 就是你实际上没有复制数组。当你写

var copyOfArray = array;

您将引用分配给同一个数组到另一个变量中。换句话说,他们都指向同一个物体。

答案 2 :(得分:10)

所以这里的每个人都做了很好的解释为什么正在发生的事情 - 我只是想放弃一条线让你知道 如何 我能够解决这个问题 - 非常容易:

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

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

Spread Syntax Source

编辑:至于为什么,并回答你的问题:

  

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

答案是在JavaScript中,数组和对象是 mutable ,而字符串和数字以及其他基元是 immutable 。当我们执行如下任务时:

  

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

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

我会建议这篇文章What are immutable and mutable data structures?深入研究这个主题。

MDN Glossary: Mutable

答案 3 :(得分:7)

我发现这是对对象或数组进行深度克隆的最简单方法:

const objectThatIWantToClone = { foo: 'bar'};
const clone = JSON.parse(JSON.stringify(objectThatIWantToClone));

通过字符串化,我们制作一个不可变的副本,然后我们可以将其转换回JSON。

https://codepen.io/Buts/pen/zWdVyv

答案 4 :(得分:5)

克隆对象 -

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

采用以下示例:

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索引上运行的操作都会产生所需的结果,但final(object)除外,因为它是通过引用复制的,它也会改变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/

欢呼声

答案 5 :(得分:4)

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

对于整数是不同的,因为它们是原始类型。

S上。

答案 6 :(得分:2)

除了原始数据类型(字符串和数字IIRC)之外,所有内容都通过引用进行复制。

答案 7 :(得分:2)

在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']

希望它有所帮助。

答案 8 :(得分:1)

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

同样,您有多个变量保持相同的数字。

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

当您编写copyOfMyArray.splice时,您修改原始数组 数字是不可能的,因为数字是不可变的,不能被修改,

答案 9 :(得分:1)

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

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;
}

答案 10 :(得分:1)

将数组复制到临时变量的另一种方法是将数组类型转换/更改为字符串,然后检索它。

例如

var a = [1,2,3];
typeof(a) (this will give "object")
var b = JSON.stringify(a);
typeof(b) (this will give "string");
b = JSON.parse(b);
typeOf(b) (this will give "object")

现在b的变化不会反映在a

答案 11 :(得分:0)

除非您克隆或复制,否则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(),这些函数将使用其他引用创建新对象。如果您知道更多功能,请告诉我,谢谢!

答案 12 :(得分:0)

你的答案就在这里

var myArray = ['a', 'b', 'c'];
var copyOfMyArray = myArray.slice();

基本上,slice()操作克隆数组并返回浅表副本。

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

可在以下链接中找到明确的文档: Array.prototype.slice()

答案 13 :(得分:0)

      var myArray = ['a', 'b', 'c'];
      var copyOfMyArray = JSON.parse(JSON.stringify(myArray));
      copyOfMyArray.splice(0,1);
      
      console.log('orginal Array',myArray)
      console.log('After Splic of copyOfMyArray',copyOfMyArray);
      //If not using the JSON.parse(JSON.stringify (array)) at the time of assign the array change of the one array will affect another because of the reference. 

答案 14 :(得分:0)

浅表复制的问题是不是所有对象都被克隆,而是被引用,所以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

答案 15 :(得分:0)

如果您有jQuery

const addItem = item => ({type: 'ADD_ITEM', item})
const removeFirstItem = () => ({type: 'REMOVE_FIRST_ITEM'})

const fifoReducer = (prev = defaultValue, action = {}) => {
  const { type, item} = action;
  switch (type) {
    case "ADD_ITEM":
      return [...prev, item];
    case "REMOVE_FIRST_ITEM":
      return prev.slice(1);
    default: {
      return prev;
    }
  }
};

是一种无需引用即可复制嵌套对象的方法。