javascript递归调用function向scope外数组push新值的时候为什么会同时改变整个数组的值?

我本来想用javascript写一段程序,把兼并在一起的一行数据扩展成多行数据。比如下面这一行

[["a1","a2","a3","a4","a5","a6","a7"],["b1","b2","b3","b4","b5"],["c1","c2","c3"]]

我希望输出成

[ [ 'a1', 'b1', 'c1' ],
  [ 'a1', 'b1', 'c2' ],
  [ 'a1', 'b1', 'c3' ],
  [ 'a1', 'b2', 'c1' ],
  ...
  [ 'a7', 'b5', 'c1' ],
  [ 'a7', 'b5', 'c2' ],
  [ 'a7', 'b5', 'c3' ] ]

大致写了一下程序:

function tableDecompress(t){
  // var t = [["a1","a2","a3","a4","a5","a6","a7"],["b1","b2","b3","b4","b5"],["c1","c2","c3"]]
  var xecimal = t.map(function(e,i,a){
    if(i<a.length-1){
      return xecimalCalc(a.slice(i+1))
    }else{
      return 1
    }
  })
  function xecimalCalc(arr){
    return arr.map(e=>e.length).reduce((a,b)=>a*b)
  }
  var ttlCell = xecimalCalc(t)
  var result= new Array(ttlCell)
  result.fill([])
  fillIt(t,0,[],[])
  function fillIt(arrOutter,iOutter,valueStore,addr){
    arrOutter[iOutter].forEach(function(e,i,a){
      var vStore = valueStore
      var addrS = addr
      vStore[iOutter]=e
      addrS[iOutter]=i
      if(iOutter<arrOutter.length-1){
        fillIt(arrOutter,iOutter+1,vStore,addrS)
      }else{
        addrSeq = addrS.map((e,i)=>e*xecimal[i]).reduce((a,b)=>a+b)
        result[addrSeq]=result[addrSeq].concat(vStore)
      }
    })
  }
  return result
}

这里面:

function xecimalCalc(arr){
    return arr.map(e=>e.length).reduce((a,b)=>a*b)
  }
  var ttlCell = xecimalCalc(t)
  var result= new Array(ttlCell)
  result.fill([])

是我后面改过来的,原先就只是声明一个var result=[],在fillIt这个函数里面else分支直接result.push(vStore),但是如果这样做的话呢,就会变成下面的结果:

 [ 'a7', 'b5', 'c3' ],
 [ 'a7', 'b5', 'c3' ] ,
 [ 'a7', 'b5', 'c3' ] ,
 [ 'a7', 'b5', 'c3' ] 
....
 [ 'a7', 'b5', 'c3' ] ]

在else分支里面我试过console.log(vStore),还把addrS也算出来了,觉得这样子总不会重复覆盖了吧,但是仍旧不是那么回事。只有当我事先建好一个刚好够长的数组,把这个数组用[]填充好,之后不用赋值而是用concat的时候才能正常运行。

实在是搞不懂到底为啥会这样?

阅读 2.8k
1 个回答

多谢评论里的指点。所以问题是出在这两行代码:

var vStore = valueStore
var addrS = addr

对object用赋值只能是传递一个reference,要想做clone需要用下面的方法:

var vStore = valueStore.slice(0)
var addrS = addr.slice(0)

修改以后可以正常运行。

function tableDecompress(t){
  var result = []
  fillIt(t,0,[],[])
  function fillIt(arrOutter,iOutter,valueStore,addr){
    arrOutter[iOutter].forEach(function(e,i,a){
      var vStore = valueStore.slice(0)
      var addrS = addr.slice(0)
      vStore[iOutter]=e
      addrS[iOutter]=i
      if(iOutter<arrOutter.length-1){
        fillIt(arrOutter,iOutter+1,vStore,addrS)
      }else{
        result.push(vStore)
      }
    })
  }
  return result
}
撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题