Javascript revealing module pattern, public properties
Asked Answered
S

2

25

I'm trying to get my head round the revealing module pattern in javascript. I'm puzzled by two things about the following code snippet.

        var Child = function () {
            var totalPoints = 100;
            addPoints = function (points) {
                totalPoints += points;
                return totalPoints;
            };
            getPoints = function () {
                return totalPoints;
            };
            return {
                points: totalPoints,
                addPoints: addPoints
            };
        };
        $(function () {
            var george = Child();
            console.log(george.points);
            console.log(george.addPoints(50));
            console.log(george.points);
        });
  1. The three values written to the console here are 100, 150, 100. That tells me that when I call "addPoints" with a value the totalPoints variable isn't updated. If I examine the value of totalPoints within the addPoints function it has been incremented properly. What's going on?

  2. If I use the console to examine window.addPoints or window.getPoints I can see that because I haven't used "var" in front of the function declarations they've been added to the global scope. Isn't that wrong? Most of the examples I'm looking at seem to do this.

Any pointers gratefully received.

Strega answered 17/1, 2012 at 10:33 Comment(0)
B
33

You're passing a number here:

return {
    points: totalPoints,
    addPoints: addPoints
};

This piece of code is no different from:

return {
    points: 100,
    addPoints: addPoints
};

You're passing the value; not a reference to totalPoints (the latter is not possible in JavaScript). So when totalPoints changes, the value in the object does not.


Using a function

The simplest way to get around this is to use a function to get the most up-to-date result (like getPoints which you already have). This JSFiddle gives a complete example:

return {
    points: function(x) { return totalPoints; }, // always up-to-date
    addPoints: addPoints
};

The downside is that the caller must now ask for points as a function call:

console.log(george.points());

Using a getters and setters

Another solution is using getters which would enable you to get an updated value with just george.totalPoints, though getters are not widely supported (yet). You could implement a getter like this:

var obj = {};

obj.points = addPoints;

// add a "special" property to the object instead of normal notation
Object.defineProperty(obj, "totalPoints", {
    get: function() { // function that's executed when you use `.totalPoints`
        return totalPoints;
    }
});

return obj;

Secondly, dropping var makes the functions global which is correct but not advisable. You could make one var statement using commas, if that's what you mean:

var totalPoints = 100, // expands to three `var` statements, so no
    addPoints = ...,   // global variables
    getPoints = ...;
Beating answered 17/1, 2012 at 10:44 Comment(2)
Thank you. This was very helpful, so I made a post about it setters and getters are needed geekswithblogs.net/Aligned/archive/2013/01/15/…. Hopefully it helps someone.Mcmasters
+1 finding a bug related to this was driving me crazy for the last 45 minutes now!Stare
A
0

This is slightly different solution, that doesn't require a getter/setter and preserves totalPoints as a property.

    var Child = function () {

        var _api;

        return _api = {

            addPoints: addPoints,
            points: 100
        };

        /* ----------- */

        function addPoints(points) {
            _api.points += points;
            return _api.points;
        };

    };
Appetence answered 20/3, 2015 at 12:31 Comment(1)
That provides a "getter like" field but not a setter - it is still a function.Ion

© 2022 - 2024 — McMap. All rights reserved.