DEV Community

Victor Hazbun
Victor Hazbun

Posted on • Updated on

The biggest JavaScript OOP gotcha

Object Oriented programming is very straightforward for most OOP languages but JavaScript is a bit different.

Given a game creator function, we want to extend the game object so it has some additional methods to increment the score game.scorePoint().

You can use es6console to run our experiments.

Let's see the code:

function GameCreator(score) {
    this.score = score;
}

GameCreator.prototype.scorePoint = function() {
    function incrementScore() {
        this.score++;
    }
    incrementScore();
};

GameCreator.prototype.endGame = function() {
    console.log(`Game has finished ${this.score}`)
};

let game = new GameCreator(0);

game.scorePoint();
Enter fullscreen mode Exit fullscreen mode

After executing this code you will notice that game.score is still 0. But Why? What happened? Is our code wrong?

Yes it's wrong (but it looks fine, right?). First let's understand why it's wrong. Turns out this from this.score++ represents the window object not the game instance. HA! Gotcha! That means our score is somewhere lost in the window.

So, the idea with this example is to understand that a nested function will not look up for the instance, in our case the game instance. Imagine for a moment that scorePoint not only has incrementScore but also printScore, but hey why not also another function endGameWhenMaXScore. See? The function could be splitted into small ones which is something great since it helps to organize the code, each function is reponsible for one little thing.

Now to fix the issue... we can use Arrow Functions:

function GameCreator(score) {
    this.score = score;
}

GameCreator.prototype.scorePoint = function() {
    const incrementScore = ()=> {this.score++};
    incrementScore();
};

GameCreator.prototype.endGame = function() {
    console.log(`Game has finished ${this.score}`)
};

let game = new GameCreator(0);

game.scorePoint();
Enter fullscreen mode Exit fullscreen mode

By using the Arrow Function we are indicating that we want to use the game instance instead of window.

Now game.score; will return 1.

Using the constructor method:

function GameCreator(score) {
    constructor (score) {
        this.score = score;
    }

    increment() {
        this.score++;
    }

    endGame(){
        console.log(`Game has finished ${this.score}`)
    }
}

let game = new GameCreator(0);

game.increment();
game.endGame();
Enter fullscreen mode Exit fullscreen mode

Using ES6 classes Classes - JavaScript | MDN:

class Game {
    constructor (score) {
        this.score = score;
    }

    increment() {
        this.score++;
    }

    endGame(){
        console.log(`Game has finished ${this.score}`)
    }

}

let game = new Game(0);

game.increment();
game.endGame();
Enter fullscreen mode Exit fullscreen mode

See the code running here

Isn't it pretty? I like it, you like it, everybody does.

So, we have learn that this can be very tricky, still manageable. You just need to understand this on each context.

Try experimenting with the this keyword on different contexts and analyze the results. It will help you understanding how it works. At the end you will avoid a lot of mistakes and you will become a better JS developer!

Top comments (5)

Collapse
 
Sloan, the sloth mascot
Comment deleted
Collapse
 
victorhazbun profile image
Victor Hazbun

Updated!

Collapse
 
alohci profile image
Nicholas Stimpson

Thanks. Also "Game has finished ${this.score}" should be a template literal `Game has finished ${this.score}`

Thread Thread
 
victorhazbun profile image
Victor Hazbun

Updated, again - also included couple other fixes (method calls).

Collapse
 
victorhazbun profile image
Victor Hazbun • Edited

Update: Includes ES6 class example codesandbox.io/s/ecstatic-poitras-...