Doing JavaScript inheritance with classy

Along with the release of ZippyUI I have also released classy, which is a JavaScript module for writing smart classes in JavaScript today. It’s also available on npm.

classy is very flexible and can help you do inheritance, mixins, overriding and some other useful stuff. But in this article I’ll be focusing on inheritance and how it helps you write clear code.

Be sure to check out the classy readme to get an overview of what classy can do for you.

Defining classes

Now let’s talk inheritance.

We’ll take a simple example, and create a shape class, with a rectangle subclass.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
var Shape = classy.define({
alias: 'shape',
//the init method is the constructor function
init: function(name){
this.name = name
},
getName: function(){
return this.name
}
})
var Rectangle = classy.define({
alias : 'rectangle',
extend: 'shape',
init: function(width, height){
this.callSuperWith('rectangle')
this.width = width
this.height = height
},
//we override the getName fn just to have and example of
//calling this.callSuper()
getName: function(){
return this.callSuper()
},
getArea: function(){
return this.width * this.height
}
})

Now let’s create a rectangle

1
2
3
var r = new Rectangle(/* width */ 5, /* height */ 10)
r.getName() == 'rectangle' // true
r.getArea() == 50 //true

In the code above notice we use classy.define in order to define a new class. Every class can get an alias, which is a string identifier associated to the class. Since we specify alias: 'shape' for the Shape class, we can do

1
2
3
4
5
classy.define({
alias: 'rectangle',
extend: 'shape' //the alias of the Shape class
//...
})

But whenever an alias is expected, a reference to the class is also accepted. So the code above is the same as

1
2
3
4
5
classy.define({
alias : 'rectangle',
extend: Shape
//...
})

Retrieving classes

Whenever you define a new class and specify it’s alias, you can always get a reference to that class later using classy.getClass

1
2
3
4
var Shape = classy.getClass('shape')
var Rectangle = classy.getClass('rectangle')
var s = new Shape('triangle')

Any class created by classy has a reference to the super class. Access it using MyClass.$superClass

Example:

1
Rectangle.$superClass === Shape

Any object instance created as an instance of a classy class has a reference to both the own class that created it and to the super class.

Example:

1
2
3
4
5
6
var r = new Rectangle()
//all lines below are true
r.constructor === Rectangle
r.$ownClass === Rectangle
r.$superClass === Shape

Instantiation

You can either create a rectangle instance by directly calling new Rectangle or by using classy.create

1
2
var r1 = new Rectangle(/* width */ 5, /* height */ 10)
var r2 = classy.create('rectangle', /* width */ 5, /* height */ 10)

The classy.create method calls new on the class specified as the first parameter, and passes all subsequent parameters to to the constructor.

Let’s define a Square shape as well

1
2
3
4
5
6
7
8
9
10
11
var Square = classy.define({
alias: 'square',
extend: 'rectangle',
init: function(size){
this.callSuperWith(size, size)
this.name = 'rectangle'
}
})
var s = classy.create(Square, 5)

Calling super methods

Calling super methods is easily accomplished by using callSuper and callSuperWith. Whenever you want to call the super method with exactly the same params as the current method, just use callSuper. If you want to provide custom params, you’ll need to use callSuperWith.

This is very similar with the use of super in ES6.

Extending functions

You can even extend functions (“classes”) not defined with classy.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
function Animal(sound){
this.sound = sound
}
Animal.prototype.makeSound = function(){
return 'I sound like this: ' + this.sound
}
var Dog = classy.define({
extend: Animal,
alias: 'dog',
init: function(){
this.callSuperWith('bark') //this calls Animal fn
}
})
var dog = new Dog()
dog.makeSound() == 'I sound like this: bark' // is true

This gives you a lot of flexibility, and you can seamlessly work with an existing code-base. classy.define simply creates a new function to be used as a constructor. This function properly inherits the prototype of the super class, and its own prototype is augmented with other properties.

You can even use classy with ES6 classes since ES6 classes are just a new pattern for writing constructor functions.

Conclusion

classy classes are fully interoperable with constructor functions and prototypes, and they give you a very nice way to do inheritance and call super methods. classy is thoroughly tested and carefully written, so it can be used with confidence. Make sure you give it a try, as it’s gaining momentum!