Javascript Class Patterns

When I first started using C++, it was still essentially a preprocessor that produced C code. I liked that because if I was really confused about a particular feature, I could examine the C code to figure out how it worked. The features that I found most interesting in C++ could be broken into two categories: Polymorphism and Encapsulation. Polymorphism is just a fancy way of saying that a given function or operation can work on different types, the way addition can be performed on both integers and floating point types. Using Overloading and Templates, you can write functions in C++ that handle all the different types you want to operate on.

Encapsulation means that something is organized in such a way that it is isolated from the rest of the code with a well-defined interface to access or modify it. It's like a black box with a set of interfaces. You just need to know what the black box is supposed to do, you don't get involved in it's internals. When programs get complicated, it's good to know that various bits and pieces do their job without interference. C++ introduced encapsulation in the form of Classes.

C didn't have Classes, but it had 'structs', which were a simple form of organized data. Structs had a collection of fields, with identifiers for each field:

struct point {
  int x;
  int y;
  int z;
};

This is an improvement over Fortran, say, where you might have an array of 3 numbers represent a point. At least this way, it's more clear what each of the fields mean. It's a nice way to organize the data, but it doesn't provide any programmatic interface, it just keeps the fields together in one object. C++, especially in it's original form, used the concept of C structs to organize the data, but added interfaces in the form of member functions (methods). It also added the ability to hide data in a private section, accessible only by member functions, and it added constructors, special methods called to initialize data when the object is created. Continuing with the example above, suppose you wanted to provide a function to move the point relative to where it is, or to goto a particular location in space. In C++ you might create a class like this:

class point {
  point() {x=0; y=0; z=0;}
  point(int i, int j, int k) {x=i; y=j; z=k;}
  void moveBy(int i, int j, int k) {x+=i; y+=j; z+=k;}
  void moveTo(int i, int j, int k) {x=i; y=j; z=k;}
private:
  int x;
  int y;
  int z;
}

The C++ code would be converted into C, with special functions for the methods and a special constructor function that was automatically called when an object was created, whether that was at the beginning of the program or more dynamically during it's execution. So it was sort of possible to do this in C, it's just that C++ made it a lot easier, and ensured that the private section was not accessible outside of member functions.

Now, Javascript doesn't have Classes, but it does have Objects which bear a superficial resemblance to C structs. They have named fields and data associated with those fields:

point = {
  x: 0,
  y: 0,
  z: 0
}

It's nice and organized, but still no interfaces akin to member functions. We can easily add functions to the object:

point = {
  x: 0,
  y: 0,
  z: 0,

  moveBy: function(i, j, k) {x+=i; y+=j; z+=k;},
  moveTo: function(i, j, k) {x=i; y=j; z=k;}
}

but this doesn't do what we want. When it tries to access 'x' (or y or z) it doesn't access point.x, but looks in the environment for a variable called 'x'. Either it finds none, and throws an error, or it finds one and updates the wrong variable.

Fortunately, there's an easy fix. 'This' is the easy fix. That is to say the 'this' special variable in Javascript. If you haven't run into it before, the details can be a bit confusing, but for our purposes, I think it's safe to assume that the 'this' variable always indicates the enclosing object instance, so we replace 'x' with 'this.x':

point = {
  x: 0,
  y: 0,
  z: 0,

  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;}
}

Now when we say point.moveTo(3,5,0), the coordinates (x,y,z) become (3,5,0). If we then call point.moveBy(10,10,10), it becomes (13,15,10). The 'this' variable ensures that when point.moveBy() and point.moveTo() are called, this.x corresponds to point.x and so on. So we've got the data and we've got member functions. This doesn't give us everything that a C++ class has, though. For that we still need constructors and data hiding. Using the same techniques we used for static variables, we should be able to accomplish that, but more about that next time.

Para obter mais informações sobre otimizações de compiladores, consulte Aviso sobre otimizações.