challenges

Copycat object (Solution)

Moxie and Gus start out with the same number of toys. But then Gus starts stealing from Moxie (he’s cute but a bit of a toy hog). Unfortunately, this code doesn’t keep track of the toy counts as expected. What’s the issue?

Code with error

const moxieToyCounts = {
  mice: 4,
  feathers: 3,
  hairTies: 25,
};
 
const gusToyCounts = moxieToyCounts;
 
// gus steals a feather
gusToyCounts.feathers += 1;
moxieToyCounts.feathers -= 1;
 
// this should be reflected in the toy counts...
//   ...but it's not
console.log(gusToyCounts.feathers) // 3
console.log(moxieToyCounts.feathers) // 3

Corrected code

const moxieToyCounts = {
  mice: 4,
  feathers: 3,
  hairTies: 25,
};
 
const gusToyCounts = {...moxieToyCounts};
 
// gus steals a feather
gusToyCounts.feathers += 1;
moxieToyCounts.feathers -= 1;
 
 
// this is now reflected in the toy counts
console.log(gusToyCounts.feathers) // 4
console.log(moxieToyCounts.feathers) // 2

Explanation

You may have heard of mutability before. Objects and arrays in JavaScript are mutable, which means a couple things things:

  1. Their properties can be updated even if they’ve been declared as a const
  2. They represent a chunk of memory, not an actual value.

Let’s tackle the second item: an object variable represents a memory location instead of an actual value. As an analogy, think of the object variable gusFeathers as a pointer to a bag of feathers (owned by Gus). If you assign another object variable to equal gusFeathers (const moxieFeathers = gusFeathers), this is not going to make a new bag of feathers. Instead, both gusFeathers and moxieFeathers lead you to the exact same bag.

So if someone takes a feather from gusFeathers, then moxieFeathers loses a feather too. They’re the same thing.

If you want to make a new bag of feathers, there are several ways to go about it. You could use the spread operator , Object.assign(), or Object.create(). Any of these make a copy of the object, with its own memory location. So const moxieFeathers = {...gusFeathers} results in two independent objects. In the “bag of feathers” analogy, someone could set fire to gusFeathers (I suspect Moxie), and moxieFeathers will remain intact.

This “pointer to a memory location” concept is also why properties can be updated, even if a mutable object is a const. When you update a property, you’re not updating the pointer to the location in memory. You’re updating what’s stored at that location. So this is fine:

const moxieToyCount = {};
moxieToyCount.hairTies = 28;

but this will throw an error:

const gusToyCount = {};
const moxieToyCount = { hairTies: 28 };
 
// Uncaught TypeError: Assignment to constant variable.
gusToyCount = moxieToyCount;

Further Reading