如何在 JavaScript 中创建一个不被引用修改的常量数组?

Ale*_*mez 0 javascript arrays node.js

我很惊讶这会发生在 javascript 中,但是当我开发算法时,我注意到了这一点。

REPEL 模式下的 node.js 程序

当我将“mt”数组放入“wm”常量中,并更改“mt”数组中的值时,该值会在“wm”数组中更改。为什么会这样?我怎样才能使这不会发生?

我问这个是因为“wm”,在我看来,应该包含“mt”数组的副本,除非我直接修改它,否则不应该改变。

T.J*_*der 5

变量不直接包含对象(包括数组),它们包含内存中其他位置的那些对象的引用。所以你开始:

                       +????????????+
mt:Ref55162???????????>| (数组) |
                       +????????????+
                       | 0: "你好" |
                       | 1: "adios" |
                       +????????????+

然后当你这样做时const wm = mt;,你只是在复制引用;两个变量(常量)都指向同一个数组:

mt:Ref55162?????+
                |
                | +????????????+
                +?????>| (数组) |
                | +????????????+
                | | 0: "你好" |
wm:Ref55162?????+ | 1: "adios" |
                       +????????????+

(该 Ref55162 值仅用于说明;您永远不会在 JavaScript 代码中看到对象引用的实际值。)

如果你想复制一个数组,你有多种选择:

  1. sliceconst wm = mt.slice();

  2. Array.fromconst wm = Array.from(mt);

  3. 传播符号: const wm = [...mt];

  4. concat[].concat(mt)

请注意,理论上#2 - #4 涉及临时对象,但如果此代码位于性能关键区域,则这些临时对象可能会被优化掉。#1 不涉及任何临时对象。(好吧,即使是最后一个,理论上规范描述了为调用创建词法环境对象,slice但在实践中,实现并不总是必须从字面上创建这些对象并在可能的情况下避免它。)

可能值得指出的是,如果您有一个包含对象引用的数组,那么就像wd = mt,上述所有内容都会为您提供一个包含引用副本的新数组。所以如果你有:

const a = [{value: 1}, {value: 2}];
const b = a.slice();
Run Code Online (Sandbox Code Playgroud)

...现在ab是不同的数组,但两个数组都包含对相同的两个对象的引用:

                +???????????????+ +?????????????????????+
a:Ref14785?????>| (数组) | | |
                +???????????????+ \ +??????????+ |
                | 0: Ref55463 |???+?>| (对象) | |
                | 1: Ref16839 |??+ +????????????+ |
                +???????????????+ | | 值:1 | |
                                 | +????????????+ |
                                 | |
                                 \ +??????????+ |
                                  +?>| (对象) | |
                                 / +??????????+ |
                                 | | 值:2 | |
                                 | +????????????+ |
                +???????????????+ | |
b:Ref98464?????>| (数组) | | |
                +???????????????+ | |
                | 0: Ref55463 |??|?????????????????????+
                | 1:Ref16839 |??+
                +???????????????+

如果您想复制这些对象,则必须故意这样做,例如:

const b = a.map(obj => ({...obj}));
Run Code Online (Sandbox Code Playgroud)

map为数组中的每个条目调用回调并从返回值构建一个新数组;{...obj}将自己的可枚举属性从对象复制到新对象中。

或者如果a是一个数组数组,你可能会这样做:

const b = a.map(array => array.slice());
Run Code Online (Sandbox Code Playgroud)

(这继续。如果内部对象或数组包含一个对象、引用,则...)

如果您想一直向下复制(“深”复制),则此问题的答案可能很有用:在 JavaScript 中深度克隆对象的最有效方法是什么?


或者,如果您希望数组不可修改,则可以freeze在其上使用:

Object.freeze(mt);
Run Code Online (Sandbox Code Playgroud)

那么你根本无法改变它。