New JavaScript* Class Patterns

Tweet Like

"You lied."
"I exaggerated."
- Saavik and Spock, The Wrath Of Khan

When I was a college freshman, I had a friend who was a senior studying chemistry. He summed up his experience saying that as a senior he learned how the chemistry professors had been lying to him all those previous years, but now he was learning the truth. Hopefully what he meant was that as his education progressed the simplifications of his earlier years were replaced by more complex models of chemical interactions.

Now of course, I am completely truthful, but sometimes I simplify. So trust me when I say that "this" and "new" are sometimes more complicated than they appear, and that despite the fact that everything in Javascript is an object, it's not quite object-oriented, though it has the necessary plumbing to support it. And I admit, there's more to Classes than member functions and public and private vars. But first things first, we still need to figure out a class pattern that works for private and public data.

As I alluded in my previous missive, we can use what we learned from the static variable pattern to provide private variables. For static variables, we took advantage of local vars in a hidden scope, while we took advantage of Javascript objects and the "this" pointer to provide a simple approximation to classes. We might be tempted to combine the two like this:

var p = function () {
    var x= 0
    var y= 0
    var z= 0

    return {
        moveBy: function (i, j, k) {this.x+=i; this.y+=j; this.z+=k;},
        moveTo: function (i, j, k) {this.x=i; this.y=j; this.z=k;},
        show: function () {console.log("Current position is",this.x,",",this.y,",",this.z)}
    }
}()

See the Pen %= penName %> by Intel IDZ (@intelidz) on CodePen

But that doesn't work at all. The "private" vars, x, y, z aren't referred to at all in the member functions, which reference this.x &c. We could try this:

var p = function () {
    this.x= 0
    this.y= 0
    this.z= 0

    return {
        moveBy: function (i, j, k) {this.x+=i; this.y+=j; this.z+=k;},
        moveTo: function (i, j, k) {this.x=i; this.y=j; this.z=k;},
        show: function () {console.log("Current position is",this.x,",",this.y,",",this.z)}
    }
}()

See the Pen %= penName %> by Intel IDZ (@intelidz) on CodePen

But that also doesn't work at all. This is where "this" gets confusing. When you refer to "this" from inside an object, it does indeed refer to that object, but when it's called from a function, it depends on how that function is called. In particular, in the global context of javascript, "this" refers to the global context (or window object). In other words, this.x is the same as the global variable x. Thus if you open up a Javascript console and run the code above, you will initialize 3 globals, x, y, and z to 0. On the other hand, the "this.x" inside the returned object is uninitialized, so incrementing it in moveBy() doesn't do anything useful. Plus, it adds three public vars to the object, so the whole point has been missed completely. On top of everything else, by calling it inplace, as we did with the static function, we don't have any way to make other instances of the same class. While we have a single point "p", we can't make any more. We should sit back and rethink this a bit.

What we want is something like a constructor, that will initialize all the appropriate members, keep private stuff private and allow access to public stuff. We should call it like a constructor, and each instance should be separate from the others. We want this constructor to return an object that will be our class instance.

Basically we want something like this:

function Point() {
    var x = 0;
    var y = 0;
    var z = 0;

    return {
        moveBy: function (i, j, k) {x+=i; y+=j; z+=k;},
        moveTo: function (i, j, k) {x=i; y=j; z=k;},
        show: function () {console.log("Current position is",x,",",y,",",z)}
    }
}

See the Pen %= penName %> by Intel IDZ (@intelidz) on CodePen

Note that now Point is the constructor, not a particular point instance. To get an instance, we call Point() to get an object representing the instance of the class Point, like so:

p1 = Point();
p2 = Point();

See the Pen %= penName %> by Intel IDZ (@intelidz) on CodePen

One last thing. Earlier I mentioned the "new" keyword. Now's when it comes in handy. It turns out that it does something very much like what our Point() function does. When it is used before a function call, it creates a new object then calls the function, setting the "this" variable to refer to the newly created object. Anything returned from that function is ignored, while the new object is returned instead. Any modifications to "this" are reflected in the returned object, like this:

function f() {
    this.x = 1;
}

> x = new f()
{ x: 1 }

See the Pen %= penName %> by Intel IDZ (@intelidz) on CodePen

This is similar to what we want for our constructor, i.e. create a new object and initialize it, except that now we can depend on the definition of 'this' and thus use it effectively:

function Point() {
    var x = 0;
    var y = 0;
    var z = 0;

    this.moveBy = function (i, j, k) {x+=i; y+=j; z+=k;},
    this.moveTo = function (i, j, k) {x=i; y=j; z=k;},
    this.show = function () {console.log("Current position is ", x,",", y,",", z)}
}
> p1 = new Point()
{ moveBy: [Function],
  moveTo: [Function],
  show: [Function] }
> p1.show()
Current position is  0 , 0 , 0
undefined
> p1.moveBy(1,2,3)
undefined
> p1.show()
Current position is  1 , 2 , 3

See the Pen %= penName %> by Intel IDZ (@intelidz) on CodePen

This gives us a nice clean pattern. Anything we want to be "private" should be a var, while anything we want "public" should be a field in "this" object. Of course, we're not done. We don't have inheritance. But we'll leave that for another day.