简体   繁体   English

为什么局部数组的推送会使全局数组突变?

[英]Why does push of local array mutate global array?

I am puzzled why this following bit of code will return mutations of both the local and global array: 我不知道为什么以下这段代码将返回局部数组和全局数组的突变:

var globalarray = [1,2,3];

 function test(){
     let localarray = globalarray;
     localarray.push(4);
     console.log(localarray);
     console.log(globalarray);
 }
 setInterval(test, 2000);

Returns: 返回值:

 [1,2,3,4] for both

My impression was that localarray would be a copy of globalarray . 我的印象是, localarray将副本globalarray I saw another answer that said in order to make a copy of an array you need to use .slice().reverse() , which seems like a workaround. 我看到另一个回答,说要复制数组,您需要使用.slice().reverse() ,这似乎是一种解决方法。 Why does it not just create a new local copy? 为什么不仅创建新的本地副本? Is there a simple and efficient way to make a local copy of a global array? 有没有简单有效的方法来创建全局数组的本地副本? Otherwise it seems like making multiple mutations to a global array is terrible for performance. 否则,对全局数组进行多次更改似乎会降低性能。

The reason for this in your code is because you are simply telling your test function to point to the globalarray with the = operator. 在您的代码中这样做的原因是因为您只是在使用=运算符告诉test函数指向globalarray This is because in JavaScript, variable assignments do not inherently "copy" objects into the new variables; 这是因为在JavaScript中,变量分配并不会固有地将对象“复制”到新变量中; this might seem confusing, so just think of the = operator as a sign that points your code to the location of an object. 这似乎令人困惑,所以只需将=运算符视为将代码指向对象位置的符号。

The only times that the = operator is making new copies is when you are working with primitive types . =运算符仅在使用原始类型时才制作新副本。 In those cases, you cannot inherently change what those objects are, so = is sufficient to make a copy like you would expect. 在这些情况下,您不能固有地更改这些对象的含义,因此=足以像您期望的那样进行复制。

The reason for the .slice().reverse() is to work around the problem you are seeing. .slice().reverse()是要解决您所遇到的问题。 Another way you could do this is by using let localarray = globalarray.map(e => e) , or as samee suggested, by using let localarray = [...globalarray] . 可以执行此操作的另一种方法是使用let localarray = globalarray.map(e => e) ,或者如建议的一样,使用let localarray = [...globalarray] The .map operator takes the function given to it as the first argument and applies it to every element, and then it stores the result in a different array at another location that is not the same as globalarray . .map运算符将赋予它的函数作为第一个参数,并将其应用于每个元素,然后将结果存储在与globalarray 不同的另一个位置的另一个数组中。

Keep in mind that these methods, and any others that might be suggested to you, are shorthand ways of doing 请记住,这些方法以及可能会向您建议的其他方法都是速记方法

let localarray = new Array(globalarray.length);
for (let i = 0; i < globalarray.length; i++) {
    localarray[i] = globalarray[i];
}

// localarray can now be freely modified because it does not point to the same array as globalarray

Also keep in mind that if you need to also create copies of the elements inside of the arrays, then you will have to use more comprehensive copying code. 还请记住,如果您还需要在数组内部创建元素的副本,那么您将不得不使用更全面的复制代码。 There are libraries that can do this sort of heavy-duty copying if you really need it. 如果您确实需要,有些库可以执行这种繁重的复制。

In JavaScript (as in many other languages), objects are passed by reference. 在JavaScript中(与许多其他语言一样),对象是通过引用传递的。 Arrays are also passed by reference (because an array is actually a type of object). 数组也通过引用传递(因为数组实际上是对象的一种类型)。 So when you say: let localarrray = globalarray , you are actually setting localarray to a pointer that resolves to globalarray . 因此,当您说: let localarrray = globalarray ,实际上是将localarray设置为解析为globalarray的指针

There are several strategies for obtaining a true copy. 有几种获取真实副本的策略。 If you're trying to get a fresh copy of the original array, the Array prototype function of .map() is one of the most targeted tools for the job. 如果您尝试获取原始数组的新副本,则.map()的Array原型函数是该工作最有针对性的工具之一。 It would look like this: 它看起来像这样:

let localarray = globalarray.map(element => element);

Simple way to clone an array is just 克隆数组的简单方法就是

let localarray = globalarray.slice();

I do it a different way to deep cloning: 我用另一种方式进行深克隆:

clone: function() {
    var clone = undefined;
    var instance = this;

    if ( XScript.is.xobject(instance) ) {
        clone = {};
        for ( var prop in instance ) {
            if ( instance.hasOwnProperty(prop) ) {
                var p = instance[prop];
                if ( XScript.is.xobject(p) ) p = p.clone();
                clone[prop] = p;
            }//END IF this
        }//END FOR prop

        return clone;
    }//END IF xobject

    if ( XScript.is.xarray(instance) ) {
        clone = instance.slice(0);
        return clone;
    }

    return clone;
}//END FUNCTION clone

This clone will require you attaching the clone object to the object prototype and check to see if its an array, object, or other. 此克隆要求您将克隆对象附加到对象原型,并检查其是否为数组,对象或其他。 I am not changing the function to fit all your needs because one should learn to somewhat how to change it and what to do instead of copy pasta. 我不会根据您的所有需求更改此功能,因为您应该学会一些如何更改它以及如何操作的方法,而不是复制面食。 Plus it is just an example. 另外,这只是一个例子。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM