Let’s implement a deck of playing cards in an object-oriented way with JavaScript. Then we’ll add methods for additional functionality such as a shuffle operation, a reset operation, and a deal operation that will return a random card and remove it from the deck.

This type of task is a very common interview question and example given by teachers when discussing object-oriented programming patterns. JavaScript is such a flexible language that even though it is prototype-based it can be used in a roughly class-based manner, especially with the introduction of class syntax in ES6 which is a syntactic-sugar over the base prototype.

Create a Deck

To start, we need to think about our requirements. There are 52 cards in a deck, which can have one of 4 suits and one of 13 values. We can store our deck as an array. By iterating over each suit and then each value we can populate it as follows:

class Deck {
  constructor() {
    this.deck = [];

    const suits = ['Hearts', 'Spades', 'Clubs', 'Diamonds'];
    const values = ['Ace', 2, 3, 4, 5, 6, 7, 8, 9, 10, 'Jack', 'Queen', 'King'];

    for (let suit in suits) {
      for (let value in values) {
        this.deck.push(`${values[value]} of ${suits[suit]}`);
      }
    }
  }
}

const deck1 = new Deck();
console.log(deck1.deck);
// ["Ace of Hearts", "2 of Hearts", "3 of Hearts", "4 of Hearts", "5 of Hearts", "6 of Hearts", "7 of Hearts", "8 of Hearts", "9 of Hearts", "10 of Hearts", "Jack of Hearts", "Queen of Hearts", "King of Hearts", "Ace of Spades", "2 of Spades", "3 of Spades", "4 of Spades", "5 of Spades", "6 of Spades", "7 of Spades", "8 of Spades", "9 of Spades", "10 of Spades", "Jack of Spades", "Queen of Spades", "King of Spades", "Ace of Clubs", "2 of Clubs", "3 of Clubs", "4 of Clubs", "5 of Clubs", "6 of Clubs", "7 of Clubs", "8 of Clubs", "9 of Clubs", "10 of Clubs", "Jack of Clubs", "Queen of Clubs", "King of Clubs", "Ace of Diamonds", "2 of Diamonds", "3 of Diamonds", "4 of Diamonds", "5 of Diamonds", "6 of Diamonds", "7 of Diamonds", "8 of Diamonds", "9 of Diamonds", "10 of Diamonds", "Jack of Diamonds", "Queen of Diamonds", "King of Diamonds"]

Shuffle a deck

For our first method, we want to randomly shuffle the deck. Mike Bostock, the creator of the D3 visualization library, has a fantastic post discussing why you need to use the Fisher-Yates Shuffle for a truly non-biased shuffle.

Using Bostock’s code for a generic shuffle function, we can add a shuffle method to our class as follows:

shuffle() {
  const { deck } = this;
  let m = deck.length, i;

  while (m) {
    i = Math.floor(Math.random() * m--);

    [deck[m], deck[i]] = [deck[i], deck[m]];
  }

  return this;
}

Note that we use Object Destructuring in the first part of our shuffle method. In other words, the following two lines are equivalent:

const { deck } = this;
const deck = this.deck;

We also use another shortcut when declaring the m and i variables to keep them on one line. The following three approaches are equivalent:

// Option 1
let m = deck.length;
let i;

// Option 2
let me = deck.length,
  i;

// Option 3
let m = deck.length, i;

Then later on we use Array Destructuring to swap i and m before finally returning this. We need to remember to call our shuffle method on our newly created deck instance deck1, too, when we want to use it.

Deal a card

Now we need to add a deal method that will return one card and remove it from the deck. Fortunately we can use the pop method to do exactly this so our code is quite concise for this one!

deal(){
  return this.deck.pop();
}

Our complete code now looks like this:

class Deck {
  constructor() {
    this.deck = [];

    const suits = ['Hearts', 'Spades', 'Clubs', 'Diamonds'];
    const values = ['Ace', 2, 3, 4, 5, 6, 7, 8, 9, 10, 'Jack', 'Queen', 'King'];

    for (let suit in suits) {
      for (let value in values) {
        this.deck.push(`${values[value]} of ${suits[suit]}`);
      }
    }
  }

  shuffle(){
    const { deck } = this;
    let m = deck.length, i;

    while(m){
      i = Math.floor(Math.random() * m--);

      [deck[m], deck[i]] = [deck[i], deck[m]];
    }

    return this;
  }

  deal(){
    return this.deck.pop();
  }
}

const deck1 = new Deck();
deck1.shuffle()
console.log(deck1.deck);
deck1.deal()
console.log(deck1.deck);

At the bottom we create a new instance deck1 of our Deck class, call shuffle() on it, and output the result. Then we deal() one card and if we look at our deck1 again we can see that the last card has been removed!

Reset

The last step is to add a reset() method that will give us a fresh, randomly shuffled deck of cards. If you think about it, we don’t really need new code for this, we just need to rearrange what we already have.

Our reset method is really just our original code for creating a basic Deck. We just need to separate it out from our constructor into its own method. As an additional bonus, we can make it so that whenever Deck creates a new object instance, that new object will automatically be shuffled.

Here’s what the code looks like:

class Deck{
  constructor(){
    this.deck = [];
    this.reset();
    this.shuffle();
  }

  reset(){
    this.deck = [];

    const suits = ['Hearts', 'Spades', 'Clubs', 'Diamonds'];
    const values = ['Ace', 2, 3, 4, 5, 6, 7, 8, 9, 10, 'Jack', 'Queen', 'King'];

    for (let suit in suits) {
      for (let value in values) {
        this.deck.push(`${values[value]} of ${suits[suit]}`);
      }
    }
  }

  shuffle(){
    const { deck } = this;
    let m = deck.length, i;

    while(m){
      i = Math.floor(Math.random() * m--);

      [deck[m], deck[i]] = [deck[i], deck[m]];
    }

    return this;
  }

  deal(){
    return this.deck.pop();
  }
}

const deck1 = new Deck();
console.log(deck1.deck);
deck1.reset();
console.log(deck1.deck);

Now each and every time we create a new Desk instance it will be randomly shuffled but we can always reset it to an ordered configuration. Plus we can deal a card or add additional shuffles as desired.