4

So I have a property in a class called 'tiles' which contains information about a checkers board game's state. I'm trying to push this property to an array called 'moves' every time I make a legal move and at the start of the game. But the problem is, every time I push the new tiles property, the previous elements in the moves array change to the values of the latest pushed tiles.

I understand this is happening because the object is being passed by reference and hence replaces old elements in array because they now point to the same object, which is the latest value of the property tiles. So with my code given below, is there a way I can push this object not by reference but each different individual state of 'tiles' that resulted due to legal moves.

Here is my snippet: App.js

App = function () {
  var self = this;
  self.tiles = []; 
  // this is populated with objects from a json file
  //code to fetch json and save it to self.tiles
  //more code

  this.startGame = function () {
    //other code
    self.moves.push(self.tiles);
  };
  this.makeMove = function () {
    //other code
    self.moves.push(self.tiles);
  };
};

So what I expect is in self.moves array, the tiles should point to different objects instead of the same object. It should contain different states of self.tiles but right now, as I push the property, the elements of 'moves' array is overwritten by the latest self.tiles value.

Any help to solve this problem will be highly appreciated. Thanks!

siddube
  • 105
  • 1
  • 2
  • 10
  • does you tile class implement the .clone() pattern? if yes use self.moves.push(self.tiles.clone()) – Erch Feb 19 '19 at 12:39
  • I guess yes, because in the callback I have the following code. self.tiles = wade.cloneObject(data.data.tiles); – siddube Feb 19 '19 at 12:47
  • Hey Erch, sorry I have not implemented the .clone() pattern and was confused about the cloneObject function I use which ships with the ES6 Game Engine. But Maheer's answer did the trick... But thank you anyway! – siddube Feb 19 '19 at 12:53
  • *"object is being passed by reference"* It's a common mistake to assume this, but JavaScript is a pass-by-value language. Objects are *reference-type* values, but that's not the same as pass-by-reference. – Felix Kling Feb 19 '19 at 21:54
  • Thanks for the correction Felix. – siddube Feb 21 '19 at 12:57

3 Answers3

4

You should use JSON.parse(JSON.stringify()) to clone a nested object.You can use Object.assign to clone shallow object

App = function () {
  var self = this;
  self.tiles = []; 
  // this is populated with objects from a json file
  //code to fetch json and save it to self.tiles
  //more code

  this.startGame = function () {
    //other code
    self.moves.push(JSON.parse(JSON.stringify(self.tiles)));
  };
  this.makeMove = function () {
    //other code
    self.moves.push(JSON.parse(JSON.stringify(self.tiles)));
  };
};
Maheer Ali
  • 35,834
  • 5
  • 42
  • 73
1

I've fiddled around with it for a bit and found you can use the spread operator like so:

var a = {b: 1, c: 2}
var array1 = []

array1.push({...a})
a.c=3

console.log(array1) // [0: {b: 1, c: 2}]
console.log(a) // {b: 1, c: 3}

MDN: Spread in object literals

Andrei C
  • 768
  • 9
  • 21
  • 1
    That only makes a shallow copy (similar to [Object.assign](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/assign) but excluding prototype). If `a` was `{level1:{level2:2}}` and you'll do `({...a}).level1.level2=8` then you'd mutate the `a` value. – HMR Feb 19 '19 at 12:57
  • so what you're saying is that the "cloned" object's props are really references to a.prop1, a.prop2? TIL – Andrei C Feb 19 '19 at 15:35
0

The only way you can address your issue is to pass the clone of the object you want to push into the vector. In such cases normally you would write a clone() method to your object that returns a deep copy of itself. The object returned by this method can be pushed to the array.

ravi
  • 131
  • 1
  • 8