Ale*_*mez 0 javascript arrays node.js
我很惊讶这会发生在 javascript 中,但是当我开发算法时,我注意到了这一点。
当我将“mt”数组放入“wm”常量中,并更改“mt”数组中的值时,该值会在“wm”数组中更改。为什么会这样?我怎样才能使这不会发生?
我问这个是因为“wm”,在我看来,应该包含“mt”数组的副本,除非我直接修改它,否则不应该改变。
变量不直接包含对象(包括数组),它们包含对内存中其他位置的那些对象的引用。所以你开始:
+????????????+
mt:Ref55162???????????>| (数组) |
+????????????+
| 0: "你好" |
| 1: "adios" |
+????????????+
然后当你这样做时const wm = mt;,你只是在复制引用;两个变量(常量)都指向同一个数组:
mt:Ref55162?????+
|
| +????????????+
+?????>| (数组) |
| +????????????+
| | 0: "你好" |
wm:Ref55162?????+ | 1: "adios" |
+????????????+
(该 Ref55162 值仅用于说明;您永远不会在 JavaScript 代码中看到对象引用的实际值。)
如果你想复制一个数组,你有多种选择:
slice: const wm = mt.slice();
Array.from: const wm = Array.from(mt);
传播符号: const wm = [...mt];
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)
...现在a和b是不同的数组,但两个数组都包含对相同的两个对象的引用:
+???????????????+ +?????????????????????+
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)
那么你根本无法改变它。