oolib.js

// The JavaScript OOP library

author: Zsolt Szloboda, Idya. Contact me on Google+
 
   

Overview

oolib.js is a tiny JavaScript library that provides an original solution to the age-old problem of JavaScript OOP encapsulation. It offers the following features:

Getting started

Download the library from here: https://github.com/idya/oolib

Include oolib.js into your HTML file with something like this:

<script src='path/to/oolib.js'></script>

This will create a global namespace object oo.

The library is also provided as an A(synchronous) M(odule) D(efinition) module (RequireJS, curl.js, etc.), and as a CommonJS module (typically for server-side environments).

The basics

We can define a class (ie a constructor function) with the following syntax:

var MyClass = oo.createClass({

	_create: function(foo) {
		this.myField = foo;
	},
	_myPrivateMethod: function(bar) {
		return this.myField + bar;
	},
	myPublicMethod: function(baz) {
		return this._myPrivateMethod(baz);
	}
});

_create is the instance initialization method (we would call it a "constructor" in java), that is called automatically, when a new instance object is created. Let's create a new instance of this class:

var myObj = new MyClass(42);

Now here is the trick: we can not access the private methods (_myPrivateMethod, _create), nor the member fields (myField) through myObj. In fact, myObj has only a single member: myPublicMethod. If we call myObj._myPrivateMethod() or myObj._create(), we will get an exception; if we read myObj._myField, we will always get undefined.

Actually, the object returned by the new operator (myObj) is not the real instance object; myObj is only a proxy object - I call it an "interface object" -, that contains proxy functions to the real public methods. The real instance object is hidden behind the curtains: it is encapsulated. Anyway, we can use myObj (the interface object) just as if it was the real instance object (with the exception of the instanceof operator; but we have a solution for that - keep reading).

As you may have noticed, we use a naming convention here: if a method name starts with an underscore, it is considered private.
Fields are always considered private: we don’t have to start field names with an underscore.
Also, all of the own members of the (real) instance object (in the sense of Object#hasOwnProperty) are considered private.

Inheritance

We can define a subclass with the following syntax:

var MySubClass = oo.createClass(MyClass, {

	_myPrivateMethod: function(bar) {
		return this.myField + bar + 1;
	}
});

There is an alternative syntax to define a subclass (a syntactic sugar):

var MySubClass = MyClass({

	_myPrivateMethod: function(bar) {
		return this.myField + bar + 1;
	}
});

The above two subclass definitions are completely equivalent.

Subclasses can access the fields and the private methods of the superclass(es) (so we would call them "protected members" in java).

Invoking overridden methods

We can invoke overridden superclass methods with this._super(...). We have to supply the method name as the first parameter:

var MySubClass = MyClass({

	_myPrivateMethod: function(bar) {
		return this._super("_myPrivateMethod", bar) + 1;
	}
});

There is also _superApply; check the reference section.

The oo.Base class

oo.Base is a class defined by the library, that we can use as a base class for our classes. It provides the following basic features:

Instance initialization and deinitialization support

If we have an inheritance chain, and all the classes in the chain have a _create method, only one of them is invoked automatically when a new instance object is created (the _create method of the subclass at the end of the chain). Of course, we can invoke the overridden _create methods with _super - but we have to do it explicitly. That's where oo.Base comes to help: It implements _create in such a way, that it calls the _init methods of all the classes in the inheritance chain (if they exist) automatically.

oo.Base also defines a public method called destroy. It implements destroy in such a way that it calls the _dispose methods of all the classes in the inheritance chain (if they exist) automatically.

var MyClass = oo.Base({

	_init: function(bar) {
		console.log("Initializing MyClass");
	},

	// define some other methods ...

	_dispose: function() {
		console.log("Deinitializing MyClass");
	}
});

var MySubClass = MyClass({

	_init: function(bar) {
		console.log("Initializing MySubClass");
	},

	// define some other methods ...

	_dispose: function() {
		console.log("Deinitializing MySubClass");
	}
});

var myObj = new MySubClass(); // will invoke MyClass#_init, MySubClass#_init, in this order

// do some work with myObj ...

myObj.destroy(); // will invoke MySubClass#_dispose, MyClass#_dispose, in this order

(The library itself does not implement any mechanisms to call the destroy method automatically.)

There is also _addDestroyFn; check the reference section.

instanceof support

Consider the following example:

var MyClass = oo.Base({

	// define some class members ...
});

var myObj = new MyClass();
 
var b = myObj instanceof MyClass; // returns false

myObj instanceof MyClass returns false, because myObj is not the real instance object, but only the interface object. So how can we check, if myObj is an interface object for MyClass?
oo.Base has an isInstanceOf method; so we can do

b = myObj.isInstanceOf(MyClass); // returns true

The library defines a marker interface class oo.Interface. All interface objects are instances of this class:

b = myObj instanceof oo.Interface; // returns true

And finally, the library provides a helper function oo.isInterfaceOf. With this function we can check, if an arbitrary object is an interface object for a class:

b = oo.isInterfaceOf(myObj, MyClass); // returns true

oo.isInterfaceOf works according to the following algorithm:

  1. It checks if myObj is an instance of the oo.Interface class. If not, it returns false.
  2. It checks if myObj has a method named isInstanceOf. If not, it returns undefined.
  3. It returns myObj.isInstanceOf(MyClass).

Reference

function oo.createClass(?BaseClass, members)

Create a new class (a new constructor function).

Parameters:

BaseClass (optional):
The superclass (constructor function) to inherit from.
members:
An object literal (a map) defining the members of the new class. All non-function members are considered private. All function members with a name starting with a _ (underscore) are considered private.

An alternative syntax for creating a subclass: MySubClass = MyBaseClass(members);

field iface

This (private) field references the interface object (the proxy object) of the instance. It is set automatically when creating instances of the class.

abstract method _create(…)

The optional (private) instance initialization method that you can implement in your classes. It is called automatically when creating instances of the class. If a subclass overrides a superclass's _create method, only the subclass's _create method is called.
It gets the parameters that you pass when you invoke the constructor function with the new operator.

(_create is implemented by oo.Base.)

method _super(methodName, arg1, …, argN)

A (private) method automatically defined for all classes created by oo.createClass(). The _super method calls the superclass method specified by the first parameter (as a string).
This is useful if you override a superclass method: with _super you can invoke the overridden method.

method _superApply(methodName, argsArray)

A (private) method automatically defined for all classes created by oo.createClass(). The _superApply method calls the superclass method specified by the first parameter (as a string).
This is useful if you override a superclass method: with _superApply you can invoke the overridden method.
Method parameters can be specified as an array.

class oo.Interface

A marker interface class for the interface (proxy) objects.

class oo.Base

A simple base class, provided by the library. It’s features are detailed in the next reference entries.

abstract method _init(…)

The optional (private) instance initialization method of the classes inherited from oo.Base, that you can implement in your classes. It is called automatically by oo.Base’s implementation of the _create method. In case of an inheritance chain, all the classes’ _init methods are called (starting from the superclasses).
It gets the parameters that you pass when you invoke the constructor function with the new operator.

method oo.Base#destroy()

A public method that you can call to deinitialize the object instance. (There is no automatic mechanism in the library to call this method).

abstract method _dispose()

The optional (private) instance deinitialization method of the classes inherited from oo.Base, that you can implement in your classes. It is called automatically by oo.Base’s implementation of the destroy method. In case of an inheritance chain, all the classes’ _dispose methods are called (starting from the subclasses).

method oo.Base#_addDestroyFn(destroyFn)

A (private) method that you can call to register a deinitializator function. The registered function will be called back automatically by oo.Base’s implementation of the destroy method.
The function call will be bound to the real instance object (ie the value of this will be the instance object.)

Uses the (private) field _destroyFns, so please don't define a class member with that name.

method oo.Base#isInstanceOf(MyClass)

A public method that checks if the real instance object (not the interface object) is an instance of MyClass.

function oo.isInterfaceOf(myObj, MyClass)

Checks if myObj is an interface object (a proxy object) for the class MyClass. Useful only if MyClass is a subclass of oo.Base (or if MyClass implements the method isInstanceOf).

comments powered by Disqus