我正在尝试创建一个音乐游戏,我必须从基本的2D阵列生成3D数组。计划是在修改2D阵列之前将2D阵列复制并粘贴到3D阵列中,如图所示:
var note3base = [
["C", "E", "G"],
["C#", "E#", "G#"],
["Db", "F", "Ab"],
["D", "F#", "A"],
["Eb", "G", "Bb"],
["E", "G#", "B"],
["F", "A", "C"],
["F#", "A#", "C#"],
["Gb", "Bb", "Db"],
["G", "B", "D"],
["Ab", "C", "Eb"],
["A", "C#", "E"],
["Bb", "D", "F"],
["B", "D#", "F#"],
["Cb", "Eb", "Gb"]
];
var note3 = new Array(4);
for (h=0;h<note3.length;h++){
note3[h] = note3base;
} //creates 4 copies of note3base in a 3d-array to be modified
for (i=0;i<note3[0].length;i++){
note3[1][i][1] = flat(note3[1][i][1]); //minor
note3[2][i][1] = flat(note3[2][i][1]);
note3[2][i][2] = flat(note3[2][i][2]); //dim
note3[3][i][2] = sharp(note3[3][i][2]); //aug
} //how did everything become the same?
现在问题似乎是for
循环似乎将方法应用于每个单独的数组(0到3)。
note3 [0] [1]的期望输出为CEG,note3 [1] [1]为C Eb G,注[2] [1]为C Eb Gb,注[3] [1] ]将是CEG#。
非常感谢任何帮助!
我在下面提供了(工作)尖锐和平坦的方法供参考:
function sharp(note){
var newnote;
if (note.charAt(1) == "#"){
newnote = note.replace("#", "x");
} else if (note.charAt(1) == "b"){
newnote = note.replace("b", "");
} else {
newnote = note + "#";
}
return newnote;
}
function flat(note){
var newnote;
if (note.charAt(1) == "#"){
newnote = note.replace("#", "");
} else {
newnote = note + "b";
}
return newnote;
}
答案 0 :(得分:3)
TL; DR ,请执行以下操作:
for (h=0;h<note3.length;h++){
note3[h] = note3base.slice(0);
}
<强>解释强>
问题来自于传递某些东西与价值之间的区别。和&#39;参考&#39;在Javascript。
将原始值分配给变量(例如a = "string";
),然后将其分配给另一个变量(例如b = a;
)时,该值将传递给b
&#39; -value&#39;:值已分配给b
,但b
引用了不同的内存部分。现在有两个&#34;字符串&#34;内存中的值,一个用于a
,另一个用于b
。
a = "string";
b = a;
a = "gnirts";
console.log(b); // "string"
这不适用于非基本类型,例如数组。这里的值通过引用传递给b
&#39;这意味着内存中仍然只有一个[1,2,3]数组,a
和{{ 1}}指着它。这意味着您更改了b
中的元素,它也将更改为a
,因为它们在内存中引用相同的数组。所以你明白了:
b
a = [1, 2, 3];
b = a;
a[0] = "hello";
console.log(b); // ["hello", 2, 3]
已更改,因为它引用内存中与b[0]
相同的位置。为了解决这个问题,我们需要在将note3base传递给另一个变量时明确地复制note3base,而不是仅仅通过引用传递它。我们可以使用a[0]
执行此操作。
修改:了解更多here
答案 1 :(得分:3)
问题是当你为这样的数组分配一个等于这样的变量时:
someVar = someArray;
...它不会复制数组,它会创建对相同数组的第二个引用。 (这适用于所有对象,而数组是一种对象。)所以在你的循环之后,你说过:
for (h=0;h<note3.length;h++){
note3[h] = note3base;
... note3
中的所有元素都引用相同的底层数组。
要制作实际副本,您可以使用循环手动复制所有元素,也可以使用.slice()
method为您制作副本:
for (h=0;h<note3.length;h++){
note3[h] = note3base.slice();
}
但这只会解决问题的一半,因为note3base
本身包含对其他数组的引用,而.slice()
只会复制这些引用。也就是说,虽然note3[0]
和note3[1]
(以及2和3)会引用不同的数组,note3[0][0]
和note3[1][0]
以及note3[2][0]
和note3[3][0]
将引用相同的["C", "E", "G"]
数组。 (等等。)
你需要所谓的“深层复制”。您可以使用嵌套循环来执行此操作:
for (h=0;h<note3.length;h++){
// create this element as a new empty array:
note3[h] = [];
// for each 3-note array in note3base
for (var k = 0; k < note3base.length; k++) {
// make a copy with slice
note3[h][k] = note3base[k].slice();
}
}
说了这么多,我认为一个更简单的方法来完成整个事情就是不要使用引用数组的note3base
变量,使其成为一个返回一个函数的函数新阵列:
function makeNote3Array() {
return [
["C", "E", "G"],
["C#", "E#", "G#"],
["Db", "F", "Ab"],
["D", "F#", "A"],
["Eb", "G", "Bb"],
["E", "G#", "B"],
["F", "A", "C"],
["F#", "A#", "C#"],
["Gb", "Bb", "Db"],
["G", "B", "D"],
["Ab", "C", "Eb"],
["A", "C#", "E"],
["Bb", "D", "F"],
["B", "D#", "F#"],
["Cb", "Eb", "Gb"]
];
}
因为函数使用数组文字,所以每次调用它时都会创建一个全新的数组数组。因此,您可以执行以下操作,无需.slice()
或嵌套循环:
var note3 = new Array(4);
for (h=0;h<note3.length;h++){
note3[h] = makeNote3Array();
}