Introduction
jTypes is the most comprehensive and robust JavaScript library for overcoming differential inheritance with prototype-based objects. Its lightweight yet powerful design provides web programmers on any platform or browser the ability to emulate classical inheritance where objects are defined by classes.
Download the latest version at
www.jTypes.com (2.0.2 at the time of this writing).
Background
If you're looking for a more down-to-earth explanation rather than the generic description or tagline, then here's the deal: jTypes will provide you with the ability to develop robust, modular, and scalable application libraries in JavaScript using encapsulation, inheritance, and polymorphism.
What exactly does that mean? You know all those keywords from languages such as C++ or C# that you started missing quite badly after you transitioned to JavaScript? You know what I'm talking about. All those beautiful modifiers of classical inheritance such as virtual
, abstract
, and override
or private
, protected
, and public
that gave you so much more control and freedom with your libraries. Well, jTypes lets you take those keywords back, so you can develop extremely powerful and robust web applications using the principals of classical inheritance.
Using the Code
The $$
global variable is used as a shorthand for the jTypes
global variable. Classes are defined using the jTypes compiler and then instantiated as shown in the following definitions:
var SomeClass = $$([String modifiers,] [Class baseClass,]
[Function constructor,] Object prototype);
var someInstance = new SomeClass();
In the following example, a class is compiled and the type reference is stored in the Person
variable. A constructor is provided to take in three arguments and set the corresponding fields. Two public
read-only fields are defined using the readonly
modifier and public
access modifier, and a protected
field is defined as well using the protected
access modifier. A public
method is defined along with a virtual public
method using the virtual
modifier. Finally, a property with both get
and set
accessors is provided:
var Person = $$(function($fName, $lName, $age)
{
this.firstName = $fName;
this.lastName = $lName;
this._age = $age;
},
{
'public readonly firstName': '',
'public readonly lastName': '',
'protected _age': 0,
'public getFullName': function()
{
return this.firstName + ' ' + this.lastName;
},
'public virtual triggerOneYearOlder': function()
{
this._age++;
},
'public age':
{
'get': function()
{
return this._age;
},
'set': function($v)
{
if ($v > 0)
this._age = $v;
}
}
});
Constructors
Constructors are special methods that are executed when an object is created. They provide a means of initializing the fields and/or properties of the new object. A constructor is called only once during instantiation and must be invoked using the new
keyword when creating the object. The base constructor may be accessed through this.__base
(note the double underscore).
Fields
Fields are variables declared directly in a class. They store data that must be accessible to multiple methods or properties. Classes may have instance
fields or static
fields using the static
modifier. Instance
fields must be declared using the types undefined
, null
, boolean
, number
, or string
. Any other type is automatically treated as null
with the exception of function
(which is used to define methods) and "simple object" (which is used to define properties). To set a field to any "reference-type" object, you must do so in the constructor to prevent cross-referencing. Instance fields may also contain the readonly
modifier, which prevents the value of the field from being altered outside of the constructor.
Methods
All variables of type function
are treated as methods unless they have the static
modifier. The JavaScript reserved word this
holds reference to the private instance of the object. However, the public
instance of the object may be accessed through this.__this
. Subsequently, the protected
base class instance of the object may be accessed through this.__base
(note the double underscores on each). Method declarations can also contain the virtual
, abstract
, override
, and sealed
modifiers to implement method overriding or forcing derived classes to write their own implementation of a method.
Properties
All variables of type "simple object" are treated as properties unless they have the static
modifier. Properties combine aspects of both fields and methods. They are used as if they are field members, which allows data to be easily accessed, yet they maintain the safety and security of methods. The property definition must contain either one or two special methods known as accessors. A get
accessor has no arguments and should return a value for the property when being accessed. A set
accessor has one argument, which is the incoming value being set to the property, and need not return a value. If only a get
accessor is provided in the definition, the property acts as a read-only field, while if only a set
accessor is provided in the definition, the property acts as a write-only field. If both accessors are provided, the property acts like a normal field member, and one accessor may specify a more restrictive access modifier as well using the private
, protected
, or public
access modifiers. Property declarations can also contain the virtual
, abstract
, override
, and sealed
modifiers to implement property overriding or forcing derived classes to write their own implementation of a property.
In the following example, a class is derived from the Person
class and the type reference is stored in the Employee
variable. Four arguments are provided to the constructor, with which the first three arguments are passed into the base constructor, while the final argument is used to set a protected
field defined in the class. A public virtual
method that overrides the base virtual
method is defined. This method also calls the base virtual
method. Finally, a property with only a get
accessor is provided, which will act as if it is a read-only field:
var Employee = $$(Person, function($fName, $lName, $age, $salary)
{
this.__base($fName, $lName, $age);
this._salary = $salary;
},
{
'protected _salary': 0,
'public override triggerOneYearOlder': function()
{
this.__base.triggerOneYearOlder();
this._salary *= 1.03;
},
'public salary':
{
'get': function()
{
return this._salary;
}
}
});
Once the jTypes classes are compiled and instantiated, they can be type-checked and casted using the following constructs:
someInstance instanceof SomeClass
someInstance.as(SomeClass)
Type-Checking
Type-checking can be performed using the instanceof
operator as in the example shown above. Even when an object is down-casted, it can still be checked for its original type using the instanceof
operator. Variables can also have their native JavaScript types checked using the $$.type(obj)
helper. This function will always return a string
from the following array of types: array
, boolean
, class
, date
, function
, instance
, number
, null
, object
, regexp
, string
, undefined
, window
. Finally, there are also helper functions for each type from the previous array such as $$.isArray(obj)
, $$.isDate(obj)
, $$.isFunction(obj)
, $$.isInstance(obj)
, and so on with the exception of object, which has the $$.isSimpleObject(obj)
helper.
Casting
Casting can be performed using the .as(obj)
method attached to every class instance as in the example shown above. There are also helper functions for a few common casting scenarios encountered when working with function arguments such as $$.asArray(obj)
, $$.asBool(obj)
, $$.asFloat(obj)
, $$.asInt(obj)
, and $$.asString(obj)
.
In the following example, both classes defined in this tip will be instantiated and tested to ensure their functionality is correct based on their jTypes definitions:
var p = new Person('John', 'Doe', 30);
console.log(p.firstName);
console.log(p.lastName);
console.log(p.age);
console.log(p._age);
p.firstName = 'Jane';
p.age = -40;
console.log(p.firstName);
console.log(p.age);
p.age = 40;
console.log(p.age);
console.log(p.getFullName());
p.triggerOneYearOlder();
console.log(p.age);
var e = new Employee(p.firstName, p.lastName, p.age, 75000);
console.log(e.firstName);
console.log(e.lastName);
console.log(e.age);
console.log(e._age);
e.firstName = 'Jane';
console.log(e.firstName);
console.log(e.salary);
e = e.as(Person);
console.log(p instanceof Person);
console.log(p instanceof Employee);
console.log(e instanceof Person);
console.log(e instanceof Employee);
console.log(p.salary);
console.log(e.salary);
e.triggerOneYearOlder();
e = e.as(Employee);
console.log(e.age);
console.log(e.salary);
All source code in this example, along with a copy of jTypes 2.0.2, can be obtained from the download link at the top of this tip.
Points of Interest
The $$.empty()
helper function will return a new empty function reference each time it is invoked. Another helper function has been provided for formatting error messages when throwing exceptions using the following syntax:
throw $$.format(String message, Object arg0, ...);
There have also been significant performance increases in the upcoming beta: