I am using John Resig\'s \"Simple JavaScript Inheritance\" to create a class that can be inherited. I am also using KnockoutJS for computed observables. The problem comes in t
You could always add them in the init
. In knockout's own examples they do their binding in the constructor.
window.mynamespace.myclass = Class.extend({
init: function() {
this.someProperty = ko.observable(10);
this.someComputedProperty = ko.computed(function() {
return this.someProperty();
}, this);
}
});
Or capturing a reference to this
and forgetting about binding:
window.mynamespace.myclass = Class.extend({
init: function() {
var self = this;
self.someProperty = ko.observable(10);
self.someComputedProperty = ko.computed(function() {
return self.someProperty();
});
}
});
To demonstrate how you'd extend the class:
window.mynamespace.myotherclass = window.mynamespace.myclass.extend({
init: function() {
// do anything myotherclass init, like another observable
this.someOtherProperty = ko.observable(10);
// incidentally you *can* have private variables with this pattern
var imPrivate = 1;
this.getImPrivate = function() {
return imPrivate;
};
// then call super (maybe with arguments, maybe just passing them)
this._super('foobar');
}
});
I've never had any experience with KnockoutJS. However I'm well versed with inheritance in JavaScript and I frown upon John Resig's "Simple JavaScript Inheritance" pattern (primarily because it doesn't have private variables). Instead I prefer using my own class pattern. I think you might find it interesting:
window.mynamespace.myclass = new Class(function (uber) {
var self = this;
function constructor() {
}
this.someProperty = ko.observable(10);
this.someComputedProperty = ko.computed(function () {
return self.someProperty();
});
return constructor;
});
You may also use your method and simply skip creating self
. Since the class is not created from an object you may use this
within your class definition (something you can't do in John Resig's "Simple JavaScript Inheritance" pattern):
window.mynamespace.myclass = new Class(function (uber) {
function constructor() {
}
this.someProperty = ko.observable(10);
this.someComputedProperty = ko.computed(function () {
return self.someProperty();
}, this);
return constructor;
});
You may also add methods directly to window.mynamespace.myclass.prototype
. That way instead of returning self.someProperty()
you may return myclass.prototype.someProperty()
. Feel free to ask me if you need any help with my class pattern.
Edit:
The Class
constructor has two parameters: a function defining the class and an optional base class to derive from. The first argument (i.e. the function) must return another function which is the constructor of the class (similar to a C++ or Java constructor).
Let's create a simple Rectangle
class:
var Rectangle = new Class(function () {
// width and height are private variables
var width;
var height;
// the class constructor accepts two arguments
function constructor(length, breadth) {
// save the parameters
width = length;
height = breadth;
}
// area is a public function
this.area = function () {
// return the area of the rectangle
return width * height;
};
return constructor; // always remember to return the constructor
});
Now you can create instances of the class Rectangle
as demonstrated in this fiddle.
Now let's do something more fun - inheritance. The second parameter of the Class
constructor is the base class to derive from. Let's create a class Square
which derives from the class Rectangle
.
// notice that the class definition function accepts a parameter called uber
var Square = new Class(function (uber) {
// return the constructor of the class Square
return function (side) {
// call the base class constructor - uber
uber(side, side);
};
}, Rectangle); // Square derives from Rectangle
This is where things get interesting. Okay so we have 4 types of data members in this class pattern: private
, public
, shared
, and static
. We have already seen private and public data members.
Shared data members are those properties defined on the prototype
of a class. They are shared by all instances of the class.
Static data members are those properties defined on the class itself. They are not inherited by the instances of the class.
In the above example when Square
derives from Rectangle
it inherits all the shared and static data members of Rectangle
. In fact we may also define a new shared or static data member on Rectangle
after Square
is defined and it will still be inherited by Square
. Let's demonstrate this concept using an example:
alert(Square.staticDataMember); // staticDataMember is undefined
Rectangle.staticDataMember = "It works!"; // define staticDataMember on Rectangle
alert(Square.staticDataMember); // staticDataMember is inherited from Rectangle
The above is for static data members. Let's see the same for shared data members:
var square = new Square(5); // create an instance of Square
alert(square.sharedDataMember); // sharedDataMember is undefined
Rectangle.prototype.sharedDataMember = 0; // define sharedDataMember on Rectangle
alert(square.sharedDataMember); // sharedDataMember is inherited from Rectangle
You may see the output of the above program in the following fiddle.
That's cool, but what about private and public data members? How are they inherited? Well, private and public data members are not inherited when we create a new class. They are inherited when create a new instance of a class. This is because different instances of the same class have different private and public data members.
When we create an instance of a derived class (like Square
) we don't inherit the private and public data members of it's base class (i.e. Rectangle
) until we call the base class constructor (i.e. uber
). Only when we call the base class constructor are the private and public data members inherited (actually only the public data members are inherited - the others are called private for a reason):
var Square = new Class(function (uber) {
return function (side) {
alert(this.area); // area is undefined
uber(side, side); // inherit public members from an instance of Rectangle
alert(this.area); // area is now defined
};
}, Rectangle);
You may see the output of the above program in the following fiddle.
Now let's create another class for kicks, and while we're at it we'll also demonstrate multilevel inheritance:
var Cube = new Class(function (uber) {
var side; // the length of a side of the cube
function constructor() {
side = arguments[0]; // save the first argument passed to the constructor
uber = uber(side); // call the base constructor and save its instance
}
this.area = function () {
return 6 * uber.area(); // return the surface area of the cube
};
this.volume = function () {
return side * uber.area(); // return the volume of the cube
};
return constructor; // remember to return the constructor
}, Square); // derive from Square
This is something new. Here we created a new area
function which shadowed the area
function defined in Rectangle
, but what if we wanted to access the base class methods from the derived class?
Well, when we call the base class constructor (i.e. uber
) it returns the instance of the base class. Since we don't need the base class constructor anymore we save the instance as uber
. Then we may call the base class area
method using uber.area
as seen in the area
and volume
functions.
You may see the output of the above program in the following fiddle.
Thus you may see that this class pattern is much more powerful than John Resig's "Simple JavaScript Inheritance" pattern, and the Class
constructor is only 47 lines of code (without being minified).