开发者问题收集

Vue.js 意外的数据和方法行为

2021-03-15
189

我有一个带有简单模板的 Vue.js 组件

<div @click="createTargets(2)">
text
</div>

脚本文件是

export default {
  name: 'test',
  data() {
    return {
      targets: [],
    };
  },
  methods: {
    createTargets(targetCount) {
      this.targets = [];
      var emptyTarget = {
          id: null,
      };
      for (var i = 0; i < targetCount; i++) {
        var targetToPush = emptyTarget;
        targetToPush.id = i;
        console.log(targetToPush.id);
        this.targets.push(targetToPush);
        console.log(this.targets);
      }
      return {};
    },
  },
}

当我单击 text 时,我得到输出

0
[{"id":1},{"id":1}]
1
[{"id":1},{"id":1}]

我不明白为什么会发生这种情况。

我希望

0
[{"id":0}]
1
[{"id":0},{"id":1}]

有什么想法吗?

3个回答

答案其实很简单,对象只初始化一次,即在将其分配给变量时。如果将此变量分配给新变量,则将同一对象引用分配给新变量。更新 Object1 将更新 Object2,反之亦然。

为了避免此行为,您可以在使用新的扩展运算符初始化 Object2 时创建对象的副本:

const targets = [];
const common = { commonProp: 'test' };

for (let i = 1; i <= count; i++) {
  const target = { ...common, id: i };
  targets.push(target);
}

this.targets = targets;

请注意,应避免在循环中改变组件的状态。即使渲染循环已优化并且实际上不会渲染 count 次,但最好还是按照示例仅改变一次属性。

另请注意,嵌套对象的行为方式相同。上述解决方案称为浅拷贝,相反,深拷贝将以递归方式抓取您的对象以复制子对象/数组。

const common = {
  commonProp: { a: 1, b: 2 }
};
const object1 = { ...common, id: 1 };
const object2 = { ...common, id: 2 };
object1.commonProp.a = 2;
console.log(object1); // { commonProp: { a: 2, b: 2 } }
console.log(object2); // { commonProp: { a: 2, b: 2 } }

为了避免此问题,您可以使用库来深度复制对象/数组,或者创建一个类或工厂函数,每次调用时都会返回一个新对象。

// factory
const createTarget = id => ({
  commonProp: { a: 1, b: 2 },
  id,
});

// class
class Target {
  constructor(id) {
    this.id = id;
    this.commonProp = { a: 1, b: 2 };
  }
}

for (let i = 1; i <= count; i++) {
  const target = createTarget(i); // or new Target(i);
  targets.push(target);
}

我希望这个解释能帮助您更好地理解这个概念。

祝你好运 ;)

BPS Julien
2021-03-15

console.log()

Please be warned that if you log objects in the latest versions of Chrome and Firefox what you get logged on the console is a reference to the object, which is not necessarily the 'value' of the object at the moment in time you call console.log() , but it is the value of the object at the moment you open the console

Don't use console.log(obj) , use console.log(JSON.parse(JSON.stringify(obj)))

Michal Levý
2021-03-15

您可以打印数组值(而不是引用):

for (var i = 0; i < targetCount; i++) {
  var targetToPush = emptyTarget;
  targetToPush.id = i;
  console.log(targetToPush.id);
  this.targets.push(targetToPush);

  // like this (es6 way):
  console.log([...this.targets]);

  // or like this (old way):
  console.log(this.targets.slice());
}

当您 console.log(this.targets) 时,您直接打印变量引用,并且它的值将在下一个循环中改变,因此它也将在浏览器控制台中更改。

当您 console.log([...this.targets]) 时,您正在打印该给定循环交互的数组值(而不是引用),并且它们将保持不变,即使数组稍后发生变化。

Arthur Borba
2021-03-15