【问题标题】:issue with calling a javascript function twice两次调用javascript函数的问题
【发布时间】:2015-06-18 02:43:55
【问题描述】:

我正在尝试编写一个相当简单的 Javascript 函数,但在迭代该函数时遇到了我不理解的行为。

我已将问题归结为以下情况。我想编写一个函数,它将一个由数组数组组成的数组作为输入,例如A = [[[1]]]。我不知道这方面的标准术语,所以我将把主数组称为“0 级”,它的元素是“1 级”的数组。我会说 1 级数组由“2 级”数组组成。 2 级数组由整数组成。

函数在输入A(0 级数组)上执行以下操作:

  1. 创建一个空数组L
  2. 对于A 中的每个级别 1 数组 M
    • M 中每个2 级数组中的每个整数条目加一;
    • M 的两个副本添加到L
  3. 返回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]]]

我不明白这种差异来自哪里。

【问题讨论】:

  • 这不是问题。两次调用的函数没有相同的结果,再次阅读他的问题。我正在调查,我认为有线索
  • 它可能是引用值而不是复制值。在 A = myFunct(A) 调用之间尝试 A = JSON.parse(JSON.stringify(A)) 。我无法测试我在 iPhone 上
  • @FlavienVolken 是的,确实可以解决它,但我不明白为什么它在这里失败了。 :o
  • 仍在挖掘,发现奇怪的行为,执行x = [[[1]]],然后多次调用myFunc(x)
  • 我认为这个问题与 var M = A[a].slice(0) 有关。由于它是一个多维数组,因此它获取对原始 A 数组的引用。然后,当您增加 M[i][j]++ 时,它会改变 A。与@TristanFoureur 在他的答案中发现的相同:)

标签: javascript arrays loops iteration


【解决方案1】:

问题出在这行:

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() 的文档:

对于对象引用(而不是实际对象),切片复制对象 对新数组的引用。原始数组和新数组都引用 到同一个对象。如果引用的对象发生更改,则更改为 对新数组和原始数组都可见。

Source

这就是为什么,因为当您执行M[i][j] 时,仍然会在外部引用更深层次的数组。

【讨论】:

    【解决方案2】:

    既然Tristan Foureur已经指出了造成差异的原因,我只是想补充一下原因

    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]]]

    【讨论】:

      【解决方案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);
      

      【讨论】:

        猜你喜欢
        • 2013-07-02
        • 1970-01-01
        • 1970-01-01
        • 2014-02-11
        • 2013-11-06
        • 2017-06-01
        • 2011-07-19
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多