Skip to content

JavaScript – Practicing Prototypes

Praciticing Prototypes Cover

In the last two articles, we solved two exercises, one based on comparisons, and the other one on closures. Our third and final exercise from the book will be on the topic of prototypes.

We picked these exercises up from Kyle Simpson’s You Don’t Know JS book series, and as I already mentioned those books might be the ultimate resource for mastering JavaScript, and I highly recommend everyone to go through all of the books in the series.

Prototypes Exercise

Define a slot machine with three reels that can individually spin(), and then display() the current contents of all the reels.

The basic behavior of a single reel is defined in the reel object below. But the slot machine needs individual reels—objects that delegate to reel, and which each have a position property.

A reel only knows how to display() its current slot symbol, but a slot machine typically shows three symbols per reel: the current slot (position), one slot above (position - 1), and one slot below (position + 1). So displaying the slot machine should end up displaying a 3 x 3 grid of slot symbols.

function randMax(max) {
    return Math.trunc(1E9 * Math.random()) % max;
}

var reel = {
    symbols: [
        "♠", "♥", "♦", "♣", "☺", "★", "☾", "☀"
    ],
    spin() {
        if (this.position == null) {
            this.position = randMax(
                this.symbols.length - 1
            );
        }
        this.position = (
            this.position + 100 + randMax(100)
        ) % this.symbols.length;
    },
    display() {
        if (this.position == null) {
            this.position = randMax(
                this.symbols.length - 1
            );
        }
        return this.symbols[this.position];
    }
};

var slotMachine = {
    reels: [
        // this slot machine needs 3 separate reels
        // hint: Object.create(..)
    ],
    spin() {
        this.reels.forEach(function spinReel(reel){
            reel.spin();
        });
    },
    display() {
        // TODO
    }
};

slotMachine.spin();
slotMachine.display();
// ☾ | ☀ | ★
// ☀ | ♠ | ☾
// ♠ | ♥ | ☀

slotMachine.spin();
slotMachine.display();
// ♦ | ♠ | ♣
// ♣ | ♥ | ☺
// ☺ | ♦ | ★

Hints:

  • Use the % modulo operator for wrapping position as you access symbols circularly around a reel.
  • Use Object.create(..) to create an object and prototype-link it to another object. Once linked, delegation allows the objects to share this context during method invocation.
  • Instead of modifying the reel object directly to show each of the three positions, you can use another temporary object (Object.create(..) again) with its own position, to delegate from.

Analysis of the Problem

We need to fill in the display() method and the contents of the reels array.

The reels array needs three individual reels. We can create them by using Object.create(...). This method will create a new object using the parameter as the prototype for that new object. The return value of the method is the newly created object.

We will create three reels using Object.create(...) inside the reels array. We will then have access to three elements, each of which is actually an instance of the reel object, that has the symbols array, the spin() and display() methods!

    reels: [
      Object.create(reel),
      Object.create(reel),
      Object.create(reel)
    ],

Now we need some logic for displaying the reels. Below we create a reels array that will store our slot machine state. We iterate through a for loop three times, and then we create the line by mapping the individual reels.

Inside the mapping function, we create a slot and then we calculate the position of the slot by adding the current length, the position subtracted by one and the current line position. The result of this will be modulo by the length of the symbols, in order to take into account the circular notion of the slot machine.

Then we call the display function.

  display() {
    let lines = [];

    for (let linePos = -1; linePos <= 1; linePos++) {
      let line = this.reels.map((reel) => {
        let slot = Object.create(reel);
        slot.position = (reel.symbols.length + reel.position + linePos) % reel.symbols.length;
        return reel.display.call(slot);
      });
      lines.push(line.join(" | "));
    }
    return lines.join("\n");
  },
};

Our final solution is in the snippet below;

function randMax(max) {
  return Math.trunc(1e9 * Math.random()) % max;
}

var reel = {
  symbols: ["♠", "♥", "♦", "♣", "☺", "★", "☾", "☀"],
  spin() {
    if (this.position == null) {
      this.position = randMax(this.symbols.length - 1);
    }
    this.position = (this.position + 100 + randMax(100)) % this.symbols.length;
  },
  display() {
    if (this.position == null) {
      this.position = randMax(this.symbols.length - 1);
    }
    return this.symbols[this.position];
  },
};

var slotMachine = {
  reels: [Object.create(reel), Object.create(reel), Object.create(reel)],
  spin() {
    this.reels.forEach(function spinReel(reel) {
      reel.spin();
    });
  },
  display() {
    let lines = [];

    for (let linePos = -1; linePos <= 1; linePos++) {
      let line = this.reels.map((reel) => {
        let slot = Object.create(reel);
        slot.position = (reel.symbols.length + reel.position + linePos) % reel.symbols.length;
        return reel.display.call(slot);
      });
      lines.push(line.join(" | "));
    }
    return lines.join("\n");
  },
};

slotMachine.spin();
slotMachine.display();
// ☾ | ☀ | ★
// ☀ | ♠ | ☾
// ♠ | ♥ | ☀

Final Words

Hope you had fun solving this exercise with me!

For more articles please click below, or check the blog.