调用 javascript 函数两次的问题
issue with calling a javascript function twice
我正在尝试编写一个相当简单的 Javascript 函数,但在迭代该函数时遇到了我无法理解的行为。
我将问题归结为以下情况。我想编写一个函数,它将一个由数组数组组成的数组作为输入,例如A = [[[1]]]
。我不知道这方面的标准术语,所以我将主数组称为 "level 0",它的元素是 "level 1" 的数组。我会说 1 级数组由 "level 2" 的数组组成。二级数组由整数组成。
该函数在输入 A
(0 级数组)时执行以下操作:
- 创建一个空数组
L
;
- 对于
A
中的每个级别 1 数组 M
M
; 中每个 2 级数组中的每个整数条目加一
- 将
M
的两个副本添加到 L
- return
L
.
这是我的代码:
function myFunc(A){
var L = [];
for(var a=0; a<A.length; a++){
var M = A[a].slice(0);
for(var i=0; i<M.length; i++){
for(var j=0; j<M[i].length; j++){
M[i][j]++;
}
}
for(var s=0; s<2; s++){
var N = M.slice(0);
L.push(N);
}
}
return(L);
}
现在我测试一下:
var A = [[[1]]];
A = myFunc(A)
在此之后,我得到 A = [[[2]],[[2]]]
,这是我所期望的。但是,假设我迭代它:
var A = [[[1]]];
A = myFunc(A);
A = myFunc(A);
然后我期望获得A = [[[3]],[[3]],[[3]],[[3]]]
,但我却得到了A = [[[4]],[[4]],[[4]],[[4]]]
。
另一方面,如果我 运行 myFunc([[[2]],[[2]]])
,我确实得到了预期的 [[[3]],[[3]],[[3]],[[3]]]
。
我不明白这种差异是从哪里来的。
问题是行:
M[i][j]++;
Node 将其保留为对 A 的切片的引用,您在执行此操作时会清楚地看到它:
x = [[[1]]];
myFunc(x);
myFunc(x);
console.log(x); // ---> [[[3]]]
对于浅拷贝,您必须使用 JSON.parse(JSON.stringify())
技巧,并证明 M 是问题所在;在 M = A[a].slice(0);
之后添加此行可以解决问题。
M = JSON.parse(JSON.stringify(M))
Mozilla 关于 Array.prototype.slice()
的文档:
For object references (and not the actual object), slice copies object
references into the new array. Both the original and new array refer
to the same object. If a referenced object changes, the changes are
visible to both the new and original arrays.
这就是为什么,因为当你这样做时M[i][j]
,更深一层的数组仍然被外部引用。
既然已经指出了造成差异的原因,我只想补充一下为什么
var A = [[[1]]];
A = myFunc(A);
A = myFunc(A);
给出了与
不同的结果
myFunc([[[2]],[[2]]])
在做myFunc([[[2]],[[2]]])
时,你基本上是在做myFunc(new Array(new Array(new Array(2))),(new Array(new Array(2)))))
。
所以当 V8 引擎提升这条线时
var M = A[a].slice(0);
它会将 A[a].slice(0)
解释为
new Array(new Array(new Array(2))),(new Array(new Array(2))))[a].slice(0)
因此每次调用时都会得到 2(来自新数组)。
如果您使用日志检查函数,可以看到这一点:
function myFunc(A){
var L = [];
console.log("A is"+ JSON.stringify(A));
for(var a=0; a<A.length; a++){
var M = A[a].slice(0);
console.log("M is"+ JSON.stringify(M) + " while A is" +JSON.stringify(A),a);
for(var i=0; i<M.length; i++){
for(var j=0; j<M[i].length; j++){
M[i][j]++;
}
}
for(var s=0; s<2; s++){
var N = M.slice(0);
L.push(N);
console.log("L.push:"+ JSON.stringify(N));
}
}
console.log("the end" + JSON.stringify(L), JSON.stringify(A));
return(L);
}
var A = [[[1]]];
A = myFunc(A);
A = myFunc(A);
var t = myFunc([[[2]],[[2]]]);
如果将 var M = A[a].slice(0);
替换为
var M = new Array(new Array(new Array(2))),(new Array(new Array(2))))[a].slice(0)
和运行通过没有A的函数,你会看到[[[3]],[[3]],[[3]],[[3]]]
.
正如其他人已经提到的,问题是 M.slice
调用只进行了浅拷贝。他们还为如何进行深度复制提供了很好的解决方案。不过,我建议您真的根本不需要复制:
var L = [];
for (var iLevel1 = 0; iLevel1 < A.length; iLevel1++)
{
for (var iLevel2 = 0; iLevel2 < A[iLevel1].length; iLevel2++)
{
for (var iLevel3 = 0; iLevel3 < a[iLevel1][iLevel2].length; iLevel3++)
L.push(a[iLevel1][iLevel2][iLevel3] + 1);
}
}
return L.concat(L);
我正在尝试编写一个相当简单的 Javascript 函数,但在迭代该函数时遇到了我无法理解的行为。
我将问题归结为以下情况。我想编写一个函数,它将一个由数组数组组成的数组作为输入,例如A = [[[1]]]
。我不知道这方面的标准术语,所以我将主数组称为 "level 0",它的元素是 "level 1" 的数组。我会说 1 级数组由 "level 2" 的数组组成。二级数组由整数组成。
该函数在输入 A
(0 级数组)时执行以下操作:
- 创建一个空数组
L
; - 对于
A
中的每个级别 1 数组M
M
; 中每个 2 级数组中的每个整数条目加一
- 将
M
的两个副本添加到L
- return
L
.
这是我的代码:
function myFunc(A){
var L = [];
for(var a=0; a<A.length; a++){
var M = A[a].slice(0);
for(var i=0; i<M.length; i++){
for(var j=0; j<M[i].length; j++){
M[i][j]++;
}
}
for(var s=0; s<2; s++){
var N = M.slice(0);
L.push(N);
}
}
return(L);
}
现在我测试一下:
var A = [[[1]]];
A = myFunc(A)
在此之后,我得到 A = [[[2]],[[2]]]
,这是我所期望的。但是,假设我迭代它:
var A = [[[1]]];
A = myFunc(A);
A = myFunc(A);
然后我期望获得A = [[[3]],[[3]],[[3]],[[3]]]
,但我却得到了A = [[[4]],[[4]],[[4]],[[4]]]
。
另一方面,如果我 运行 myFunc([[[2]],[[2]]])
,我确实得到了预期的 [[[3]],[[3]],[[3]],[[3]]]
。
我不明白这种差异是从哪里来的。
问题是行:
M[i][j]++;
Node 将其保留为对 A 的切片的引用,您在执行此操作时会清楚地看到它:
x = [[[1]]];
myFunc(x);
myFunc(x);
console.log(x); // ---> [[[3]]]
对于浅拷贝,您必须使用 JSON.parse(JSON.stringify())
技巧,并证明 M 是问题所在;在 M = A[a].slice(0);
之后添加此行可以解决问题。
M = JSON.parse(JSON.stringify(M))
Mozilla 关于 Array.prototype.slice()
的文档:
For object references (and not the actual object), slice copies object references into the new array. Both the original and new array refer to the same object. If a referenced object changes, the changes are visible to both the new and original arrays.
这就是为什么,因为当你这样做时M[i][j]
,更深一层的数组仍然被外部引用。
既然
var A = [[[1]]];
A = myFunc(A);
A = myFunc(A);
给出了与
不同的结果myFunc([[[2]],[[2]]])
在做myFunc([[[2]],[[2]]])
时,你基本上是在做myFunc(new Array(new Array(new Array(2))),(new Array(new Array(2)))))
。
所以当 V8 引擎提升这条线时
var M = A[a].slice(0);
它会将 A[a].slice(0)
解释为
new Array(new Array(new Array(2))),(new Array(new Array(2))))[a].slice(0)
因此每次调用时都会得到 2(来自新数组)。
如果您使用日志检查函数,可以看到这一点:
function myFunc(A){
var L = [];
console.log("A is"+ JSON.stringify(A));
for(var a=0; a<A.length; a++){
var M = A[a].slice(0);
console.log("M is"+ JSON.stringify(M) + " while A is" +JSON.stringify(A),a);
for(var i=0; i<M.length; i++){
for(var j=0; j<M[i].length; j++){
M[i][j]++;
}
}
for(var s=0; s<2; s++){
var N = M.slice(0);
L.push(N);
console.log("L.push:"+ JSON.stringify(N));
}
}
console.log("the end" + JSON.stringify(L), JSON.stringify(A));
return(L);
}
var A = [[[1]]];
A = myFunc(A);
A = myFunc(A);
var t = myFunc([[[2]],[[2]]]);
如果将 var M = A[a].slice(0);
替换为
var M = new Array(new Array(new Array(2))),(new Array(new Array(2))))[a].slice(0)
和运行通过没有A的函数,你会看到[[[3]],[[3]],[[3]],[[3]]]
.
正如其他人已经提到的,问题是 M.slice
调用只进行了浅拷贝。他们还为如何进行深度复制提供了很好的解决方案。不过,我建议您真的根本不需要复制:
var L = [];
for (var iLevel1 = 0; iLevel1 < A.length; iLevel1++)
{
for (var iLevel2 = 0; iLevel2 < A[iLevel1].length; iLevel2++)
{
for (var iLevel3 = 0; iLevel3 < a[iLevel1][iLevel2].length; iLevel3++)
L.push(a[iLevel1][iLevel2][iLevel3] + 1);
}
}
return L.concat(L);