In JS Burner Framework, writing application is more like Java. Framework does not use Javascript’s native class system. An important reason of using framework’s own class system is framework focuses on extended classes and super functions inside an instance.
JS Burner Framework uses AMD to write class modules.
createClass
main factory function to create classes. Methods are defined as properties of an object to pass it to createClass
. To use properties, you access them with this.set(propertyName, value)
and this.get(propertyName)
methods. These methods are also placed automatically. After these examples, how to import createClass module will be explained.
var Animal = createClass({
'init': function(name){
//Constructor...
this.set('name', name); //Not "this.name = name"
console.log('New ' + name + ' born!');
},
'move': function(){
var name = this.get('name');
console.log(name + ' is moving...');
},
'sleep': function(){
console.log(this.get('name') + ' is sleeping...');
}
})
After create a class, Class.new()
function is placed automatically into class object to create instance of that class. If you notice that, new Class()
is not the way to create instances for classes. You also pass arguments to init (constructor) function with this .new()
function.
var animal = Animal.new('dog'); //Not "new Animal()"
/**
* Console:
* New dog born!
*/
animal.move();
/**
* Console:
* dog is moving...
*/
After create a class, like .new()
function, there is also .extend()
function to extend the class. So you don’t need “createClass” module anymore to extend a class, if you have. If you overwrite a method, you can call parent’s method with this.super.method()
. If you overwrite constructor method, you can call it just with this.super()
var Dog = Animal.extend({
'init': function(){
this.super('dog'); //Not "super()" w/o "this."
},
'bark': function(){
console.log('Bark bark!');
},
'sleep': function(){
this.super.sleep();
console.log('It is dreaming...');
}
});
var dog = Dog.new();
/**
* Console:
* New dog born!
*/
dog.bark(); //New method
/**
* Console:
* Bark bark!
*/
dog.move(); //Inherited method
/**
* Console:
* dog is moving...
*/
dog.sleep(); //Overwritten method
/**
* Console:
* dog is sleeping...
* It is dreaming...
*/
var Golden = Dog.extend({
'sleep': function(){
this.super.super.sleep(); //This is also possible, it will bypass the middle class
}
});
var golden = Golden.new();
/**
* Console:
* New dog born!
*/
golden.sleep();
/**
* Console:
* dog is sleeping...
*/
Classes also can be implemented by interface(s). Interface structure is quite simple. It’s array of method names.
var interface = [
'read',
'write',
'open',
'close'
];
And to implement it just put a .implement()
call to tail of class. .implement()
method also returns class itself, so you don`t need to apply it at another line.
var Class = createClass({
'read': function(){},
'write': function(){},
'open': function(){},
'close': function(){}
}).implement(interface);
If one the methods defined in interface is missed, it will throw an error and warn the developer. Implementation also traces inherited methods, so if super class has a method which interface has, you don`t need to overwrite that method in new extended class.
JS Burner framework uses Asynchronous Module Definition. And this is an example to write appropriate classes in Burner Framework.
createClass
module is placed in core
directory, and if you install framework under burner
directory, you can define a class like this;
define(['burner/core/createClass'], function(createClass){
return createClass({
'init': function(){
//Constructor...
}
})
})
When you create or extend a class, these methods will be placed automatically;
.new(...arguments)
Creates an instance.extend({})
Extends and creates new class.implement({})
Implements an interface and returns class itselfWhen you create an instance of a class, these methods will be placed automatically;
.isInstanceOf(Class)
Check the instance is an instance of given Class or one of extended classes of given class..isImplementedBy(Interface)
Check the instance is an instance of a Class implemented by given Interface..set(propertyName, value)
Sets a property of instance and returns instance itself..get(propertyName)
Returns a property of instance..unset(propertyName)
Deletes a property of instance..inc(propertyName[, increment])
It is a variant of .set()
method. It increases the value of property, so property should be a number. Default value of increment is 1
..ref
This is special property of instances. This gives the most evaluated (extended) model of instance (if class has been extended). So with this reference, base class also can reach extended class’ methods.While writing classes, when you use this
keyword, it will let you access just current and inherited super methods, not for methods defined in extended class later. Also if you overwrite a method in an extended class, and when you call an inherited method, and if that method calls another method which you has overwritten, it will call its own layer’s method, not overwritten method.
That’s why, if you write methods which returns instance itself, you have not to return this
so that when you call an inherited method, that will return its layer and after that method, you can’t access extended methods.
It’s an example to show wrong usage;
var A = createClass({
'aFunction': function(){
return this;
}
});
var B = A.extend({
'anotherFunction': function(){
return this;
}
});
var b = B.new();
//Case 1, works
b.anotherFunction().aFunction();
//Case 2, does not work
b.aFunction().anotherFunction();
In case 1, anotherFunction
returned this
as B
class, so next function reached aFunction
because it is inherited to B
.
In case2, aFunction
returned this
as A
class, and anotherFunction
is not defined or inherited in A
, so it will throw an error.
To solve this cases, there is a special property of all instances: this.ref
. This reference always gives last version of instance, so all methods can return instance successfully.
This is a correct example;
var A = createClass({
'aFunction': function(){
return this.ref;
}
});
var B = A.extend({
'anotherFunction': function(){
return this.ref;
}
});
var b = B.new();
//Case 1, works
b.anotherFunction().aFunction();
//Case 2, works
b.aFunction().anotherFunction();
Using this approach brings with it some responsibilities. You can use methods by putting them as tail to instance. However, in class methods you should not to use this approach. Because there may be an extended class(es) and they may overwrite some methods. In that case they may return incorrect instance layer to react a method.
It is an incorrect example;
var A = createClass({
'funA': function(){
console.log('A - funA');
return this.ref;
},
'funB': function(){
console.log('A - funB');
return this.ref;
},
'doIt': function(){
return this.funA().funB(); //Problematic
}
});
var B = A.extend({
'funB': function(){
console.log('B - funB');
return this.ref;
}
});
var b = B.new();
b.doIt();
/**
* Console:
* A - funA
* B - funB
*/
As you see, when we call .doIt()
after funA
it called class B
’s funB
, not A
’s. So while writing classes, you should not use tails approach.
Correct class A.doIt
method definition;
function(){
this.funA();
this.funB();
return this.ref;
}
or it is also can be used;
function(){
this.funA();
return this.funB();
}
Written with StackEdit.