返回数组时对象数组的对象属性发生变化

Object array's object properties changing when returning array

我有一个函数可以操作和创建一个对象数组,并为数组中的每个项目添加一个 position 键,但它似乎根本无法正常工作...

我的函数:

var fillQueue = function(choices, currentQueue) {
  var choice, i, positionInQueue, previousTitle, resultQueue;

  resultQueue = [];
  previousTitle = "";

  i = 0;

  while (i < 10) {
    positionInQueue = i + 1;
    console.log('Adding song to position ' + positionInQueue);

    if (currentQueue[i]) {
      previousTitle = currentQueue[i].title;
      currentQueue[i].position = positionInQueue;
      resultQueue.push(currentQueue[i]);
    } else {
      choice = choices[Math.floor(Math.random() * choices.length)];

      if (choice.title !== previousTitle) {
        previousTitle = choice.title;
        choice.position = positionInQueue;
        resultQueue.push(choice);
      } else {
        choice = choices[Math.floor(Math.random() * choices.length)];
        previousTitle = choice.title;
        choice.position = positionInQueue;
        resultQueue.push(choice);
      }
    }

    i++;
  }

  return resultQueue;
};

如果调用正确,代入 choicescurrentQueue 的值(currentQueue 也可以是一个空数组),如下所示,函数 returns 一个数组,这是有意的,但出于某种原因,它里面的每个对象上的 position 键都乱七八糟。

var test = fillQueue([ {title: '1'}, {title: '2'}, {title: '3'}, {title: '4'} ], [ { title: '5', position: 1 } ]);

上面的变量将包含这个:

[ { title: '5', position: 1 },
  { title: '1', position: 9 },
  { title: '3', position: 7 },
  { title: '1', position: 9 },
  { title: '2', position: 10 },
  { title: '2', position: 10 },
  { title: '3', position: 7 },
  { title: '1', position: 9 },
  { title: '1', position: 9 },
  { title: '2', position: 10 } ]

如您所见,它没有将 position 正确添加到每个对象中。返回数组中的每个对象都应该有 i + 1position,但它似乎是从 1 到 10 的某个随机数 - 有些对象甚至具有相同的 position

我试过了:

这让我很困惑。将不胜感激。

Fiddle: https://jsfiddle.net/deansheather/jnw8jdf4/

我能够想出以下内容。这是有效的,因为我避免使用 for/while 循环,它在 JS 中有引用 "gotchas",你在使用它更新值时需要注意。

var fillQueue = function(choices, currentQueue) {
  // concat choices to end of currentQueue
  // create a new array instance with updated position property using .map
  // keep 10 elements from new array starting at index 0 
  return currentQueue.concat(choices).map( (e, i) => {
    return Object.assign({title:e.title, position:i+1});
  }).splice(0, 10)
};

您正在将引用推入数组,因此每次更新 choice.position 时,数组中对同一选项的所有引用都将获得相同的位置值。

要解决此问题,请复制 choice 对象,更新其位置并将其推入数组,例如

if (choice.title !== previousTitle) {
        previousTitle = choice.title;
        newChoice = objCopyShallow(choice); // see below for copy function
        newChoice.position = positionInQueue;
        resultQueue.push(newChoice);
}

对于这个应用程序,一个简单的复制函数是:

function objCopyShallow(obj) {
  return Object.keys(obj).reduce(function(acc, v) {
      acc[v] = obj[v];
      return acc;
    },{});
}

如果您想了解更深入的内容,这里有一百万个关于 "how to copy an object" 的问题和答案。

正如 RobG 已经说过的,您正在推送对 array.So 的引用,创建您的选择对象的副本,然后推送到您的数组。使用 clone() 创建对象的副本 将您的任务更改为 choice = clone(choices[Math.floor(Math.random() * choices.length)]); 你完成了。

function clone(obj) {
    if (null == obj || "object" != typeof obj) return obj;
    var copy = obj.constructor();
    for (var attr in obj) {
        if (obj.hasOwnProperty(attr)) copy[attr] = obj[attr];
    }
    return copy;
}