Certified Associate JavaScript Programmer study materials
Certified Associate JavaScript Programmer study resources

Shared Wishlist Array on the Prototype

Certified Associate JavaScript Programmer · Put your knowledge to work in context.

All case studies

Shared Wishlist Array on the Prototype

A small wishlist feature uses a constructor and puts the items array on the prototype:

function Wishlist(owner) {
  this.owner = owner;
}
Wishlist.prototype.items = [];

const ana = new Wishlist('Ana');
const ben = new Wishlist('Ben');
ana.items.push('lamp');
console.log(ben.items); // ['lamp']

Both users see the same list. A developer checks ana.hasOwnProperty('items') and gets false, then proposes moving items into the constructor. Another developer wants to keep the prototype version because "it saves memory." A third symptom appears after a reset function does ana.items = []: Ana's list becomes empty, but Ben's list still contains the lamp. The team must decide where items belongs and explain why the reset appeared to work for one instance only.

1. Explain the property lookup that causes `ben.items` to contain Ana's lamp.

Read the worked answer

Wishlist.prototype.items = [] creates one array object on the prototype. Instances do not get their own items property from the constructor; only owner is assigned. When code reads ben.items, the property is not found on ben, so the prototype chain is followed and the same prototype array is returned.

ana.items.push('lamp') first reads ana.items. That lookup also finds the prototype array, because ana has no own items. push then mutates that shared array. Ben's lookup finds the same array, so Ben sees the lamp. The mutation does not create an own property on Ana; it changes the shared prototype object.

hasOwnProperty('items') is false for both instances, which confirms the property is inherited. This is not a special JavaScript bug; it is the normal prototype chain plus a mutable reference value.

2. Why did `ana.items = []` empty Ana's list but not Ben's? What does that reveal about assignment versus mutation?

Read the worked answer

Assignment to a property does not follow the prototype chain to mutate the inherited value. ana.items = [] creates a new own property on ana whose value is a fresh array. That own property shadows the prototype's items for all future lookups on ana. Ben still has no own items, so Ben keeps resolving to the shared prototype array, which still contains the lamp.

This reveals the difference between mutating an inherited object and assigning over it. ana.items.push(...) reads the inherited array and mutates it, affecting everyone. ana.items = [] writes an own property, affecting only Ana. A later delete ana.items would remove the shadow and make Ana see the shared array again.

The apparent reset was therefore local, not global, and it masked the underlying shared-state bug.

3. Where should `items` be initialized, and how should the team reason about the memory argument?

Read the worked answer

Initialize items inside the constructor:

function Wishlist(owner) {
  this.owner = owner;
  this.items = [];
}

Each instance then gets its own array. The prototype can still hold methods and immutable defaults. The memory argument for the prototype is valid for functions and shared constants, but not for mutable per-instance state such as a list. Putting an empty array on the prototype saves one array allocation and causes every instance to share mutations, which is almost never the intended semantics.

If the team wants a shared default, it must treat it as immutable and replace it rather than mutate it. A safer design is a factory function that closes over a private array, or a class field initialized per instance. The key judgement is to distinguish state that belongs to each object from behavior that can be shared. owner is already per-instance; items belongs in the same category.