4

我试图理解我对 JavaScript 对象的困惑。具体来说,我有兴趣找出导致对象引用中断的原因(如果有的话)。

为了演示这种现象,我提供了 Chrome 的 JavaScript 控制台的一些输出的副本。请注意,我在这里使用数组,但考虑到 JS 中数组和对象之间的细微区别,我们希望对象的行为相似。为了清楚起见,我添加了评论。

  // Set x to some array literal
> x = [1, 2, 3, 4, 5]
  [1, 2, 3, 4, 5]

  // Set y to x
> y = x
  [1, 2, 3, 4, 5]

> x
  [1, 2, 3, 4, 5] // as expected

> y
  [1, 2, 3, 4, 5] // as expected

如上所示,两者都x输出y期望值。现在我使用一个名为shuffle(在这个问题的底部指定)的函数来洗牌 x 的值。

  // Shuffle x
> x = shuffle(x)
  [5, 1, 4, 2, 3]

> x
  [5, 1, 4, 2, 3] // x changes as expected

> y
  [5, 1, 4, 2, 3] // y changes as expected

同样,一切都按上述预期工作。变量xy保持对同一对象的引用。然而,当我们重复这个操作时,结果却很奇怪。

  // Shuffle x
> x = shuffle(x)
  [3, 1, 5, 4, 2]

> x
  [3, 1, 5, 4, 2] // x changes as expected

> y
  [5, 1, 4, 2, 3] // y didn't change this time

下面是 shuffle 函数,改编自这里。它的目的是打乱数组(参数r1)的内容并返回n混合数组的第一项。

function shuffle(r1,n) {

  var i = r1.length, j, tempi, tempj, r2;
  r2 = r1;

  while (--i) {
    j = Math.floor(Math.random() * (i + 1));
    tempi = r2[i];
    tempj = r2[j];
    r2[i] = tempj;
    r2[j] = tempi;
  }

  return r2.slice(0,n);
}

我已经通过基于这个函数重写我的 shuffle 函数解决了这个问题。但是,我仍然想了解发生了什么。为了快速查看实际代码,我制作了一个jsFiddle

有任何想法吗?我很感激你的时间。

4

2 回答 2

4

如果您删除.slice(0,n);,它将按照您期望的方式运行。slice创建一个新数组。

所以第一次调用 shuffle 时,在循环中修改数组x = y = r1 = r2。然后在最后一行复制它并将其分配给 x。现在x !== y,但它们包含完全相同的元素。您可以在第一次调用 shuffle: 后测试它们是否是不同的对象。

下次您调用 shuffle 时,您正在对x您制作的副本进行洗牌,并且y不会受到影响。

于 2013-04-28T01:55:08.110 回答
3

.slice()制作数组的浅表副本,因此您正在x使用新数组覆盖。

// The original was shuffled, but now `x` is a new Array
x = shuffle(x);

这就是为什么y显示第一次洗牌(因为你还没有切片它),但此后没有。随后的 shuffle 在 overwritten 上x,并且y仍然引用原始文件。


如果您想截断原始数组,只需更改其.length.

所以代替这个:

return r2.slice(0,n);

做这个:

r2.length = n;

...尽管您n当前没有传递任何东西。

于 2013-04-28T01:55:33.183 回答