0

I am trying to flush some data by replacing it entirely with an object that has a set of keys with empty values.

e.g.

const sportPrototype = {
  name: '',
  players: '',
  displacement: '',
  points: '',
  leagues: []
}

var profileScratchpadOne = {
  sportScratchpad: {
    name: 'Soccer',
    players: '16',
    displacement: 'Foot',
    points: 'unlimited',
    leagues: ["Fifa"]
  }
}

profileScratchpadOne.sportScratchpad = sportPrototype
profileScratchpadTwo.sportScratchpad = sportPrototype

Whenever a value in either sportScratchpads gets changed, it does in both profileScratchpadOne and profileScratchpadTwo.

I figure a reference is being passed.

I have investigated spread operator, prototypes, constructors, and have yet to find a bulletproof, concise approach.

What is the most succinct way of getting around this, and passing a fresh object every time?

softcode
  • 4,358
  • 12
  • 41
  • 68
  • If a shallow copy is enough (as would be for the example, except maybe for the leagues property -- depending on your expectations), then `= Object.assign({}, sportPrototype);` – trincot Feb 12 '17 at 20:26

3 Answers3

2

Although Object.assign would work here, the objects would still share the same leagues array. Only primitive-typed properties will live a separate life. You could find a function that performs a deep copy, but I think in this case you could use the constructor pattern:

function SportPrototype() {
  this.name = '';
  this.players = '';
  this.displacement = '';
  this.points = '';
  this.leagues = [];
}

var profileScratchpadOne = {};
var profileScratchpadTwo = {};

var o = new SportPrototype();
o.name = 'Soccer';
o.players = '16';
o.displacement = 'Foot';
o.points = 'unlimited';
o.leagues = ["Fifa"];
profileScratchpadOne.sportScratchpad = o;

profileScratchpadOne.sportScratchpad = new SportPrototype();
profileScratchpadTwo.sportScratchpad = new SportPrototype();

Now the last two assignments will produce objects which are completely independent.

trincot
  • 317,000
  • 35
  • 244
  • 286
1

Edit : Since there is at least an object (array) on sportPrototype, shallow copy is not the right choice here. Simplest deep copy can be chosen :

function deepCopy(o) {
  return JSON.parse(JSON.stringify(o));
}

const sportPrototype = {
  name: '',
  players: '',
  displacement: '',
  points: '',
  leagues: []
}

var profileScratchpadOne = {
  sportScratchpad: {
    name: 'Soccer',
    players: '16',
    displacement: 'Foot',
    points: 'unlimited',
    leagues: ["Fifa"]
  }
}

profileScratchpadOne.sportScratchpad = deepCopy(sportPrototype);
profileScratchpadTwo.sportScratchpad = deepCopy(sportPrototype);
Bulent Vural
  • 2,630
  • 1
  • 13
  • 18
1

As others have mentioned, Object.assign() is one way to create a shallow copy of an object. Another option is to use Object.create(), which "wraps" the object so that assigning values to the properties of the new object will not overwrite the properties of the prototype:

const sportPrototype = {
  name: '',
  players: '',
  displacement: '',
  points: '',
  leagues: []
}

var profileScratchpadOne = {
  sportScratchpad: {
    name: 'Soccer',
    players: '16',
    displacement: 'Foot',
    points: 'unlimited',
    leagues: ["Fifa"]
  }
}

var profileScratchpadTwo = {}

profileScratchpadOne.sportScratchpad = Object.create(sportPrototype)
profileScratchpadTwo.sportScratchpad = Object.create(sportPrototype)

console.log(sportPrototype.name);
console.log(profileScratchpadOne.sportScratchpad.name);
console.log(profileScratchpadTwo.sportScratchpad.name);

profileScratchpadOne.sportScratchpad.name = 'Jai Alai'
profileScratchpadTwo.sportScratchpad.name = 'Curling'

console.log(sportPrototype.name);
console.log(profileScratchpadOne.sportScratchpad.name);
console.log(profileScratchpadTwo.sportScratchpad.name);

However, both Object.create and Object.assign will have a problem with the leagues property because it is a reference type and will therefore be shared between all copies if you mutate it (by adding elements, etc.). For that reason, you'll essentially need to create a new, empty array for leagues (and any other reference-type properties) when you want to copy the prototype.

You could take care of this with a factory function, like:

function sportDefaults() {
    var newObj = Object.create(sportPrototype);
    newObj.leagues = [];

    return newObj;
}

profileScratchpadOne.sportScratchpad = sportDefaults()

Edit: The benefit of Object.create is that there is only one copy of your original prototype and new properties are created as needed, which saves on memory. If you're not all that concerned about memory, you could just create a function that returns a brand new copy of your prototype every time. This would sidestep the above-mentioned issues with reference types:

function sportDefaults() {
  return {
    name: '',
    players: '',
    displacement: '',
    points: '',
    leagues: []
  }
}

var profileScratchpadOne = {}
var profileScratchpadTwo = {}

profileScratchpadOne.sportScratchpad = sportDefaults()
profileScratchpadTwo.sportScratchpad = sportDefaults()

console.log('- before assignment')
console.log(profileScratchpadOne.sportScratchpad.name)
console.log(profileScratchpadTwo.sportScratchpad.name)

profileScratchpadOne.sportScratchpad.name = 'Jai Alai'
profileScratchpadTwo.sportScratchpad.name = 'Curling'

console.log('- after assignment')
console.log(profileScratchpadOne.sportScratchpad.name)
console.log(profileScratchpadTwo.sportScratchpad.name)
JLRishe
  • 99,490
  • 19
  • 131
  • 169
  • Thanks for best addressing the use case. Something like Array.create? – softcode Feb 12 '17 at 20:39
  • 1
    @softcode You can just use an empty array: `profileScratchpadOne.sportScrachpad.leagues = []`. – JLRishe Feb 12 '17 at 20:44
  • 1
    @softcode Added an addendum with an additional approach you could use. – JLRishe Feb 12 '17 at 21:35
  • This is a great answer, however I don't understand the memory discrepancy . How is there only one copy of the prototype with Object.create? – softcode Feb 12 '17 at 21:48
  • @softcode You should read up on how the [prototype chain](https://developer.mozilla.org/en/docs/Web/JavaScript/Inheritance_and_the_prototype_chain). Basically, the objects created with `Object.create` start off with no properties of their own, but they are associated with a prototype behind the scenes that is used to provide the properties that the objects don't have. If you assign a value to a property, then that property is added to the object, but the prototype behind the scenes remains unchanged. In this way, memory only needs to be used for the objects' properties that are different.. – JLRishe Feb 13 '17 at 09:55
  • from the prototype. Everything else can be deferred off to the prototype. – JLRishe Feb 13 '17 at 09:55