With the various advancements in JavaScript/ECMAScript (I'm just going to call it JS from now on), it has gone from being a toy language only useful for putting small actions into web pages to being a language useful enough to write web services and desktop applications in. You can even write an entire application stack using just JS. To make things even more interesting, every week it seems someone comes up with yet another library for doing something a little, or even a lot easier in JS. The sheer power of JS is reflected in how quickly new libraries are cropping up making many things so much easier to do without incurring too much of a performance penalty. Remember, JS is still just a scripting language.
Background
One of the sore points for many JS developers is the seemingly weak support for objects. Sure, objects are first class entities, but the common notions of encapsulation and inheritance normally attributed to objects in other languages just aren't present. That's because JS is a prototype-oriented language as opposed to object-oriented in other object supporting languages. If you wanted to compare JS's object support to that of an object-oriented language, you'd call JS objects either instantiable namespaces or simply public structures.
What makes JS development interesting, however, is that functions are also first-class objects, in 2 different senses. First, the definition of a function is an object of type Function. Second, the runtime environment of a function is an object called a closure. Unlike normal objects, closures are more akin to private structures, whose members are only accessible to methods created in that closure. Between closures and objects, it is possible to construct objects in a fashion that more resembles ones found in object-oriented languages. However, the syntax for doing so is, well, less than elegant. Also, for all this trouble, there is no protected scope. So using this fairly standard method, we can have our objects and inheritance, but we lose out on some of the encapsulation.
The loss of encapsulation over inheritance and poor syntax aren't the only issues. The ever popular 'this' reference doesn't seem to be able to maintain its identity as a reference to an instance of the class unless you're really careful.
Other Solutions
While it may seem like I'm just ranting, I'm not the only one to see this as an issue. One of the developers at Microsoft cooked up
TypeScript to solve this problem. It's not a bad solution, if you don't mind having to compile scripting code, that is. Also, there's the little issue about compatibility with existing JS libraries. There's a way to overcome those issues, creating declaration files that define the types TypeScript expects to know when looking at various library methods and values. This works for most cases as long as you can either find a pre-written definition that's current for the version of the library you're using, or write that definition yourself. I'll just leave it that this was not the solution for me.
How about
CoffeeScript? Well, no. That, while it offers some interesting features, departs from Javascript syntax in an attempt to provide more syntatic sugar for common JS patterns. What does that mean? While it (arguably)improves on the syntax, you don't get much in the way of new functionality where objects are concerned. So once again, not the solution for me.
Prototype? Close, but no cigar. You have no idea how much I wanted this to be the solution. However, I still can't get a reasonably protected scope from it. Also, IMHO, you don't get much of a boost in the syntax for the features you get. Is it useful? Certainly, but it's just not far enough into the solution I was looking for. There are even more attempts to make a better class than these, but these are the top 3 on my list.
My Solution
After shopping around and coming out sorely disappointed, I started writing my own library. What should a class declaration look like? How can I use closures to make a functional protected scope? Like the Grinch, I puzzed and puzzed till my puzzler was sore. Then it hit me. To make a functional protected space that can be shared between classes, all I needed to do was somehow pass the object backwards through the inheritance chain and let the ancestor classes attach their own protected portions! Easier said than done. Took me 10 days to work out the final solution. The solution required WeakMap support, but there's a shim for that. So this should work in any ES5 compatible environment. I'm still working on ES4 compatibility. So without further ado:
The Class.js Library
There are 4 files in the library:
- Class.js
- WeakMap.js
- Enum.js
- Functor.js
Enum.js provides enumeration support. While not strictly identical to enums found in object oriented languages, I think this code provides something appropriate for JS. Functor.js basically does the same work as Function.bind() except that the binding can be altered and/or duplicated. WeakMap is the shim I spoke of before. Since there is no way of creating a perfect WeakMap in JS code, I settled for emulating the spec as closely as possible leaking only a GUID string for each element declared in the WeakMap. Short of that leak, the entire WeakMap spec is upheld.
The library can be used directly as <script> includes or via CommonJS. When using <script>, be sure that Class.js is last. The order of the other 3 is irrelevant. When using CommonJS, you need only require Class.JS unless you intend to directly make use of the other 3 objects.
Class.js Syntax
One of the things I wanted was an elegant syntax for creating classes that mimicked the syntax I'd grown accustom to in object-oriented languages. In most object-oriented languages, a class definition looks something like this:
class Sample {
private static final SOME_CONSTANT = SOMEVALUE;
private int instance_var;
protected void doSomething() { ... }
public Sample() { ... }
public int doSomethingElse() { ... };
}
Now one of the problems with this in JS is that "class" is a keyword. It's completely unused, but it's reserved nonetheless. Then there's the privilege levels and status modifiers that precede the member declaration. Here's how the same thing looks using Class.js.
var Sample = new Class("Sample", {
SOME_CONSTANT: Private(Static(Final(SOMEVALUE))),
instance_var: Private(undefined),
doSomething: Protected(function doSomething() { ... }),
Constructor: Public(function() { ... }),
doSomethingElse: Public(function doSomethingElse() { ... })
}
The two declarations are completely identical in meaning. With the help of Class.js, almost anything you can do in an object oriented language becomes possible in JS, and all without a transcompiler. I'll introduce the usage of this library in
part 2.