ease.js is a Classical Object-Oriented framework for JavaScript, intended to eliminate boilerplate code and “ease” the transition into JavaScript from other Object-Oriented languages. Features include:
Download v0.1.1- Simple and intuitive class definitions
- Classical inheritance
- Abstract classes and methods
- Interfaces
- Access modifiers (public, protected and private)
- Static and constant members
ease.js is a framework, not a compiler. It may be used wherever JavaScript may be used, including with other compilers/parsers. ease.js also provides support for older, pre-ES5 environments by gracefully degrading features (such as visibility support), but remaining functionally consistent.
Simple and Intuitive Class Definitions ¶
Class definitions closely resemble the familiar syntax of languages like Java and PHP.
var Class = easejs.Class;
var Stack = Class( 'Stack',
{
'private _stack': [],
'public push': function( value )
{
this._stack.push( value );
},
'public pop': function()
{
return this._stack.pop();
},
} );
Classes can be anonymous or named, the latter being more useful for debugging. Since classes may be anonymous, constructors are styled after PHP.
var Foo = Class(
'private _name': '',
__construct: function( name )
{
this._name = ''+( name );
},
'public sayHello': function()
{
return this._name + " says 'Hello!'";
},
);
Classes can be instantiated with or without the new keyword. Omission aids in concise method chaining and the use of temporary instances.
var inst = Foo( "John Doe" ); var inst = new Foo( "John Doe" ); // temporary instance Foo( "John Doe" ).sayHello();→ Read more in manual ↑
Classical Inheritance ¶
Classes can be extended to create subtypes. Like C++, methods are not virtual by default. In Java terminology, all methods are final by default. Multiple inheritance, like Java, is unsupported (see Interfaces).
var Cow = Class( 'Cow',
{
'virtual public tip': function()
{
return "Omph.";
},
} );
var SturdyCow = Class( 'SturdyCow' )
.extend( Cow,
{
'override public tip': function()
{
return "Moo.";
},
} );
Alternatively, if creating an anonymous subtype, the supertype's extend() method may be used.
var SturdyCow = Cow.extend( { /*...*/ } );
Type checks for polymorphic methods may be performed with Class.isA(), which is recommended in place of instanceof.
var cow = Cow(),
sturdy = SturdyCow();
Class.isA( Cow, cow ); // true
Class.isA( SturdyCow, cow ); // false
Class.isA( Cow, sturdy ); // true
Class.isA( SturdyCow, sturdy ); // true
To prevent a class from being extended, FinalClass may be used.
var Foo = FinalClass( 'Foo',
{
'public describe': function()
{
return "I cannot be extended.";
},
} );
→ Read more in manual
↑
Abstract Classes and Methods ¶
If a class contains abstract members, it must be declared as an AbstractClass. Abstract methods must be overridden by subtypes and are implicitly virtual.
var Database = AbstractClass( 'Database',
{
'public connect': function( user, pass )
{
if ( !( this.authenticate( user, pass ) ) )
{
throw Error( "Authentication failed." );
}
},
// abstract methods define arguments as an array of strings
'abstract protected authenticate': [ 'user', 'pass' ],
} );
var MongoDatabase = Class( 'MongoDatabase' )
.extend( Database,
{
// must implement each argument for Database.authenticate()
'protected authenticate': function( user, pass )
{
// ...
},
} );
→ Read more in manual
↑
Interfaces ¶
ease.js supports the Java concept of Interfaces, which act much like abstract classes with no implementation. Each method is implicitly abstract. Properties cannot be defined on interfaces.
var Filesystem = Interface( 'Filesystem',
{
'public open': [ 'path', 'mode' ],
'public read': [ 'handle', 'length' ],
'public write': [ 'handle', 'data' ],
'public close': [ 'handle' ],
} );
Concrete classes may implement one or more interfaces. If a concrete class does not provide a concrete implementation for every method defined on the interface, it must be declared an AbstractClass.
var ConcreteFilesystem = Class( 'ConcreteFilesystem' )
.implement( Filesystem ) // multiple interfaces as separate arguments
{
'public open': function( path, mode )
{
return { path: path, mode: mode };
},
'public read': function( handle, length )
{
return "";
},
'public write': function( handle, data )
{
// ...
return data.length;
},
'public close': function( handle )
{
// ...
return this;
},
} );
Polymorphic methods may check whether a given object implements a certain interface.
var inst = ConcreteFilesystem(); Class.isA( Filesystem, inst ); // true→ Read more in manual ↑
Access Modifiers ¶
All three common access modifiers — public, protected and private — are supported, but enforced only in ECMAScript 5 and later environments.
var DatabaseRecord = Class( 'DatabaseRecord',
{
'private _connection': null,
__construct: function( host, user, pass )
{
this._connection = this._connect( host, user, pass );
},
'private _connect': function( host, user, pass )
{
// (do connection stuff)
return { host: host };
},
'protected query': function( query )
{
// perform query on this._connection, rather than exposing
// this._connection to subtypes
},
'protected escapeString': function( field )
{
return field.replace( "'", "\\'" );
},
'public getName': function( id )
{
return this._query(
"SELECT name FROM users WHERE id = '" +
this._escapeString( id ) + "' LIMIT 1"
);
},
} );
In the above example, the database connection remains encapsulated within DatabaseRecord. Subtypes are able to query and escape strings and external callers are able to retrieve a name for a given id. Attempting to access a private or protected member externally will result in an error. Attempting to access a private member from within a subtype will result in an error.
→ Read more in manual ↑Static and Constant Members ¶
Static members are bound to the class itself, rather than a particular instance. Constants are immutable static members (unlike languages like PHP, they may use any access modifier). In order to support both pre- and post-ECMAScript 5 environments, the syntax requires use of a static accessor method — $().
var Cow = Class( 'Cow',
{
'const LEGS': 4,
'private static _number': 0,
__construct: function()
{
// __self refers to the class associated with this instance
this.__self.$( '_number' ) = this.__self.$( 'number' ) + 1;
},
'public static create': function()
{
return Cow();
},
'public static getNumber': function(){
{
return this.__self.$( '_number' );
},
} );
Cow.$( 'LEGS' ); // 4
Cow.getNumber(); // 0
Cow.create();
Cow.getNumber(); // 1
→ Read more in manual
↑