JClass
Advanced JavaScript inheritance model providing genuine private members based on John Resig's Inheritance Class
https://npmjs.org/package/jclass
Synopsis
JClass is a lightweight and flexible JavaScript inheritance model providing genuine private
members using closures. It's applicable in nodejs and normal web browsers. JClass is
implemented using prototype
's and brings along multiple features like e.g. propper (and even
inheritance consistent)instanceof
calls. To come straight to the point:
var Animal = JClass.extend({
init: function(name) {
this.name = name;
},
eat: function() {
return this.name + ' eats';
},
__behave: function() {
return this.name + ' behaves';
}
});
var Cat = Animal.extend({
init: function(name, color) {
this._super(name);
this.color = color;
this.__lives = 9;
},
eat: function() {
var msg = this.__behave();
return msg + ' because ' + this._super() + ' fish';
},
getLives: function() {
return this.__lives;
}
});
var simon = new Cat('simon', 'white');
console.log(simon instanceof Cat);
console.log(simon instanceof JClass);
console.log(simon.name);
console.log(simon.__behave());
console.log(simon.eat());
console.log(simon.__lives);
console.log(simon.getLives());
This little example shows only the basics. It's even possible to customize and optimize JClass for
the various needs of your code by using options
(e.g.
you can change the naming scheme of private members). Take a look at the examples
to get a picture of what is possible and how it's working.
Installation
For node (using npm):
npm install jclass
For web browsers:
<script src="/path/to/jclass.min.js"></script>
Examples
Calling _super
methods
var Vehicle = JClass.extend({
init: function(type) {
this.type = type;
},
drive: function() {
return 'driving';
}
});
var Car = Vehicle.extend({
init: function(color) {
this._super('car');
this.color = color;
},
drive: function() {
return this._super() + ' on 4 tires';
}
});
var myCar = new Car('red');
console.log(myCar.drive());
Private members and the private object
var Vehicle = JClass.extend({
init: function(type, price) {
this.type = type;
this.__price = price
this.__.consumption = 'too much';
},
__drive: function() {
return 'driving';
}
});
var Car = Vehicle.extend({
init: function(color, price) {
this._super('car', price);
this.color = color;
},
getPrice: function() {
return this.__price;
},
forceDrive: function() {
return this.__drive();
}
});
var myCar = new Car('red', 10000);
console.log(myCar.__price);
console.log(myCar.getPrice());
console.log(myCar.__.consumption);
console.log(myCar.__drive());
console.log(myCar.forceDrive());
Disable tracking
var opts = {
tracking: false
};
var Vehicle = JClass.extend({
init: function(type, price) {
this.type = type;
this.__price = price
this.__.consumption = 'too much';
},
__drive: function() {
return 'driving';
}
}, opts);
var Car = Vehicle.extend({
init: function(color, price) {
this._super('car', price);
this.color = color;
},
forceDrive: function() {
return this.__drive();
}
});
var myCar = new Car('red', 10000);
console.log(myCar.__price);
console.log(myCar.__drive());
console.log(myCar.forceDrive());
Final classes using extendable
var opts = {
extendable: false
};
var Vehicle = JClass.extend({
init: function(type) {
this.type = type;
this.__secret = 'yay!';
}
}, opts);
var CarHack = Vehicle.extend({
init: function() {
this._super('car');
},
getSecret: function() {
return this.__secret;
}
});
Changing keys
var opts = {
ctorName : 'ctor',
superName : '_parent',
privatePattern: '/^__.+__$/',
privateName : '_private'
};
var Vehicle = JClass.extend({
ctor: function(type) {
this.type = type;
this.__secret__ = 'yay!';
this._private.foo = 'bar';
},
__drive__: function() {
return 'driving';
}
});
var Car = Vehicle.extend({
ctor: function(color) {
this._parent('car');
this.color = color;
},
tellSecrets: function() {
return this.__drive__() + ' ' + this.__secret__;
}
});
var myCar = newCar('red');
console.log(myCar.tellSecrets());
Configuration
Options
The signature of extend
is
var SubClass = JClass.extend(properties [, options]);
-
properties
- (Object, mandatory)
- An object that defines the methods of your class. See John Resig's Simple Inheritance technique for more details.
-
options
- (Object, optional)
- An object containing
-
extendable
- (Boolean, default: true)
- When true, another subclass can inherit from this class. When false, this class cannot be
subclassed (e.g. like final
classes in Java).
-
ctorName
- (String, default: 'init')
- The name of the method that is invoked when a new instance of your class is created via
new MyClass()
. All passed arguments are applied to this method (OO speaking: the constructor's
name).
-
superName
- (String, default: '_super')
- The name of the methods of the super-class.
When you call (e.g.) this._super()
in your init
method, the init
method of the super-class
is called.
-
enablePrivacy
- (String, default: true)
- When false, this is equivalent to privatePattern = null
plus privateName = null
(see
below).
-
privatePattern
- (RegExp, default: /^__.+/)
- A regular expression that defines how your
private members look like. /^__.+/
would find __fooFn
but not _barFn
. Null means that
there will be no private members.
-
privateName
- (String, default: '__')
- In addition to values/methods that match the privatePattern
, another private object named
privateName
is visible in every method's scope. In some cases, you may use this object instead
of using tracking
(see below). Null means that no additional private object will be used.
-
tracking
- (Boolean, default: true)
- tracking
means that all members of an instance are compared before and after a method call
in order to track down any addition or deletion of private values (not methods!). When you call
(e.g.) 'this.__foo = 123' inside a method, the key __foo
(that also matches the
privatePattern
) was obviously added and will be detected by the before/after comparison. If
your classes have a lot of members, you should use the additional private object defined by
privateName
to reduce the number of comparisons. Note: when false, all non-methods (even if
they match the 'privatePattern') won't be private (this.__foo
in our example).
-
methodsKey
- (String, default: '_jcMethods_')
- The name of the object that holds all private methods during a method call. Note: you only
need to change this value in case of a name collision with your code.
-
depthKey
- (String, default: '_jcDepth_')
- The name of the depth value (0 for JClass, 1 for the first derived class, 2 for the second
...). Note: you only need to change this value in case of a name collision with your code.
-
callerDepthKey
- (String, default: '_jcCallerDepth_')
- The name of the depth value of the initial caller. Note: you only need to change this value
in case of a name collision with your code.
Please note: when extending an existing class, please use identical values for the options
ctorName
, superName
, privatePattern
and privateName
in order to keep the prototype/inheritence model
consistent!
JClass.noConflict()
Use this method when JClass
interfers with your code. It returns a reference to the JClass
base
object and resets the JClass
variable to its initial value.
Development
Authors
Marcel R. (riga)