Grauw’s blog
Object-oriented programming in JavaScript
A long time ago I posted about object oriented programming and creating classes in JavaScript. That being a bit outdated, and me knowing a lot more about JavaScript now, I decided to make another post on that topic.
It is possible to produce OOP class-like definitions using JavaScript functions and prototypes. What follows is a description of syntax, best practices and oddities of this. The syntax used here uses no external libraries.
Update: I changed the constructor function notation from var MyClass = function() {}; to function MyClass() {}. The latter will have a name property set on it which is useful for debugging and on par with the native JS objects (e.g. Array.name == "Array"). It also has the benefit of being available in the entire scope. Additionally, I removed the public properties exception.
Creating classes
JavaScript classes and methods are created in the following manner:
function MyClass() {
// constructor
}
MyClass.prototype.myMethod = function() {
// method
};
MyClass.myStaticMethod = function() {
// class method (also known as static method)
};
Convention for class names is to start with an upper-case letter (CamelCase), and members start with a lower-case letter (camelCase).
You instantiate an object using the new operator: new MyClass().
Documenting classes
Class and member documentation is created using JSDoc annotations, like so:
/**
* Constructor description
* @param option The first argument
* @class
* Class description
*/
function MyClass(option) {
// constructor
}
/**
* Method description
* @param {Array} list The first argument
* @return {boolean} The result
*/
MyClass.prototype.doSomething = function(list) {
// method
};
It is recommended to create these always. The example’s method documentation also shows optional type annotations. Primitive types are written using lowercase: number, boolean and string, and object types as one would expect: MyClass, Array, Function and Object.
Creating object properties
Object properties should always be initialised in the constructor, so that you can use the constructor as a reference of which properties the object has, and so that the object is always in a defined state:
function MyClass() {
this.myProperty = null;
}
MyClass.prototype.getMyProperty = function() {
return this.myProperty;
};
MyClass.prototype.setMyProperty = function(value) {
return this.myProperty = value;
};
Properties should generally be considered private; some people like to make this explicit by using a _ prefix. This is for reasons of encapsulation; modern JavaScript runtimes do support property getters and setters but these do not limit visibility nor do they have convenient syntax. So Java’s methodology of creating getSomething() and setSomething() getter/setter methods is adopted. Encapsulating internal state using accessor is essential to be able to deal with changing requirements, refactoring and performance optimisation in a straightforward manner and without compatibility problems.
Extending classes
In JavaScript you can not express inheritance directly. To inherit from another class you need a small extend() utility function:
function MyClass() {
MyBaseClass.call(this); // call super
}
extend(MyBaseClass, MyClass);
MyClass.prototype.doSomething = function(someValue) {
MyBaseClass.prototype.doSomething.call(this, someValue); // call super
};
/**
* Class extension utility function
* @param base The base class
* @param constructor The constructor function for the new class
*/
function extend(base, constructor) {
var prototype = new Function();
prototype.prototype = base.prototype;
constructor.prototype = new prototype();
constructor.prototype.constructor = constructor;
}
Here, MyClass inherits from MyBaseClass, and MyClass’s constructor invokes the parent object’s constructor. The doSomething() method shows how to call a base method.
You can check whether something is an instance of a certain class using the instanceof operator, e.g. new MyClass() instanceof MyBaseClass ? 'yes' : 'no'.
Grauw
Comments
None.
