### Install documentation build dependencies on Debian Source: https://www.gnu.org/software/easejs/manual/easejs.html Install Texinfo and LaTeX packages needed to build the manual. Run this on Debian-based systems before using the 'doc' target. ```bash $ sudo apt-get install texlive texinfo ``` -------------------------------- ### createClass method wrapping example Source: https://www.gnu.org/software/easejs/manual/easejs.html Simplified implementation of ease.js class creation and method wrapping. It shows how methods are bound to a visibility object using closures and apply(). Note the warning that property copying is by reference. ```javascript /** * Simple function that returns a prototype ("class"), generated from the * given definition and all methods bound to the provided visibility object */ function createClass( vis, dfn ) { var C = function() {}, hasOwn = Object.hasOwnProperty; for ( name in dfn ) { // ignore any members that are not part of our object (further down // the chain) if ( hasOwn.call( dfn, name ) === false ) { continue; } // simply property impl (WARNING: copies by ref) if ( typeof dfn[ name ] !== 'function' ) { C.prototype[ name ] = dfn[ name ]; continue; } // enclose name in a closure to preserve it (otherwise it'll contain // the name of the last member in the loop) C.prototype[ name ] = ( function( mname ) { return function() { // call method with the given argments, bound to the given // visibility object dfn[ mname ].apply( vis, arguments ); }; } )( name ); } return C; }; var vis = { _data: "foo" }, Foo = createClass( vis, { getData: function() { return this._data; }, } ); var inst = new Foo(); // getData() will be bound to vis and should return its _data property inst.getData(); // "foo" ``` -------------------------------- ### Emulating references with getters/setters Source: https://www.gnu.org/software/easejs/manual/easejs.html JavaScript example using getters and setters to forward property access to another object, emulating a reference. Demonstrates that obj2.val reflects changes to obj1.val and vice versa. ```javascript var obj1 = { val: 1 }, obj2 = { get val() { return obj1.val; }, set val( value ) { obj1.val = value; }, } ; obj2.val; // 1 obj2.val = 5; obj1.val; // 5 obj1.val = 6; obj2.val // 6 ``` -------------------------------- ### Visibility Escalation Example Source: https://www.gnu.org/software/easejs/manual/easejs.html Shows how to escalate protected members to public in ease.js. The example defines a class with protected virtual members and a subclass that overrides them as public. Note that making a public member protected would throw an error. ```javascript var Foo = Class( { 'virtual protected canEscalate': 'baz', 'virtual protected escalateMe': function( arg ) { console.log( 'In escalateMe' ); }, 'virtual public cannotMakeProtected': function() { } } ), SubFoo = Foo.extend( { /** * Escalating a property means redefining it */ 'public canEscalate': 'baz', /** * We can go protected -> public */ 'public escalateMe': function( arg ) { // simply call the parent method this.__super( arg ); } } ); ``` -------------------------------- ### Basic JavaScript class without ease.js Source: https://www.gnu.org/software/easejs/manual/easejs.html Illustrates the traditional JavaScript approach to creating a class-like object using a constructor function and prototype methods. This example is provided to highlight the limitations of the prototypal paradigm that ease.js aims to address. ```javascript /** * Declaring "classes" WITHOUT ease.js */ // our "class" var MyClass = function() { this.prop = 'foobar'; } // a class method MyClass.prototype.getProp = function() { return this.prop; }; // create a new instance of the class and execute doStuff() var foo = new MyClass(); console.log( foo.getProp() ); // outputs "foobar" ``` -------------------------------- ### Static member example using the factory pattern Source: https://www.gnu.org/software/easejs/manual/easejs.html Defines a BigBang class with static methods fromBraneCollision and fromBigCrunch, a static property _count, and a static method getTotalCount. The constructor increments the count using this.__self.$. The example creates two instances and logs the total count. ```javascript var BigBang = Class( 'BigBang', { /** * Number of big bangs that has occurred * @type {number} */ 'private static _count': 0, /** * String representing the type of big bang * @type {string} */ 'private _type': '', /** * Create a new big bang from the collision of two membranes * * @return {BraneSet} the set of branes that collided * * @return {BigBang} new big bang */ 'public static fromBraneCollision': function( brane_set ) { // do initialization tasks... return BigBang( 'brane', brane_set.getData() ); }, /** * Create a new big bang following a "Big Crunch" * * @param {BigCrunch} prior crunch * * @return {BigBang} new big bang */ 'public static fromBigCrunch': function( crunch ) { // do initialization tasks... return BigBang( 'crunch', crunch.getData() ); }, /** * Returns the total number of big bangs that have occurred * * @return {number} total number of big bangs */ 'public static getTotalCount': function() { return this.$('_count'); } /** * Construct a new big bang * * @param {string} type big bang type * @param {object} data initialization data * * @return {undefined} */ __construct: function( type, data ) { this._type = type; // do complicated stuff with data // increment big bang count this.__self.$( '_count', this.__self.$('count') + 1 ); }, } ); // create one of each var brane_bang = BigBang.fromBraneCollision( branes ), crunch_bang = BigBang.fromBigCrunch( crunch_incident ); console.log( "Total number of big bangs: %d", BigBang.getTotalCount() ); // Total number of big bangs: 2 ``` -------------------------------- ### Dog class with encapsulated behavior Source: https://www.gnu.org/software/easejs/manual/easejs.html Example class demonstrating encapsulation with private, protected, and public members. The walk() method orchestrates leg movements and standing, hiding the complexity from the caller. ```javascript Class( 'Dog', { 'private _legs': {}, 'private _body': {}, // ... 'public walk': function() { this.stand(); this._moveFrontLeg( 0 ); this._moveBackLeg( 1 ); this._moveFrontLeg( 1 ); this._moveBackLeg( 0 ); }, 'protected stand': function() { if ( this.isSitting() ) { // ... } }, 'public rollOver': function() { this._body.roll(); }, 'private _moveFrontLeg': function( leg ) { this._legs.front[ leg ].move(); }, 'private _moveBackLeg': function( leg ) { this._legs.back[ leg ].move(); }, // ... } ); ``` -------------------------------- ### Basic inheritance example with Dog, LazyDog, and TwoLeggedDog Source: https://www.gnu.org/software/easejs/manual/easejs.html Shows how to define a parent class Dog with virtual public methods and create subclasses using both named (Class('LazyDog').extend(Dog, {...})) and anonymous (Dog.extend({...})) syntax. The override keyword is used to override the parent's walk() method. ```javascript // our parent class (supertype) var Dog = Class( 'Dog', { 'virtual public walk': function() { console.log( 'Walking the dog' ); }, 'public bark': function() { console.log( 'Woof!' ); } } ); // subclass (child), as a named class var LazyDog = Class( 'LazyDog' ).extend( Dog, { 'override public walk': function() { console.log( 'Lazy dog refuses to walk.' ); } } ); // subclass (child), as an anonymous class var TwoLeggedDog = Dog.extend( { 'override public walk': function() { console.log( 'Walking the dog on two feet' ); } } ); ``` -------------------------------- ### Server-side include with require('easejs') Source: https://www.gnu.org/software/easejs/manual/easejs.html Use this to include ease.js in a CommonJS-compliant system such as Node.js. Assumes ease.js is installed in a location accessible to require.paths (e.g., via npm). The require() loads the index.js file in the easejs/ directory and assigns its exports to the easejs variable. ```javascript /** example-include.js **/ var easejs = require( 'easejs' ); ``` -------------------------------- ### Correctly prototypally extending GNU ease.js classes Source: https://www.gnu.org/software/easejs/manual/easejs.html Demonstrates the correct way to use a class as a prototype by calling the asPrototype method, which instantiates the class without executing the constructor. The subtype's constructor must invoke the parent constructor to ensure consistent state. ```javascript var Foo = Class( { /*...*/ } ); // extending class as a prototype function SubFoo() { // it is important to call the constructor ourselves; this is a // generic method that should work for all subtypes, even if SubFoo // implements its own __construct method this.constructor.prototype.__construct.apply( this, arguments ); // OR, if SubFoo does not define its own __construct method, you can // alternatively do this: this.__construct(); }; SubFoo.prototype = Foo.asPrototype(); // Correct SubFoo.prototype.constructor = SubFoo; ``` -------------------------------- ### List ease.js source tree directories Source: https://www.gnu.org/software/easejs/manual/easejs.html Commands to change into the ease.js source directory and list its top-level directories, showing doc/, lib/, test/, and tools/. ```shell $ cd easejs $ ls -d */ doc/ lib/ test/ tools/ ``` -------------------------------- ### Clone ease.js repository from GitHub or Gitorious Source: https://www.gnu.org/software/easejs/manual/easejs.html Use these commands to clone the GNU ease.js source repository. Choose one of the two commands, not both. The repository will be cloned into the ./easejs directory. ```shell # to clone from GitHub (do one or the other, not both) $ git clone git://github.com/mikegerwitz/easejs # to clone from Gitorious (do one or the other, not both) $ git clone git://gitorious.org/easejs/easejs.git ``` -------------------------------- ### Using the abstract factory via dependency injection Source: https://www.gnu.org/software/easejs/manual/easejs.html Shows how to instantiate Dialog with different concrete factories, allowing the widget library to be injected at runtime. This enables adding or removing libraries without modifying the Dialog class. ```javascript // we can use whatever widget library we wish by injecting it into // Dialog Dialog( JqueryUiWidgetFactory() ).show(); Dialog( DojoWidgetFactory() ).show(); Dialog( YuiWidgetFactory() ).show(); ``` -------------------------------- ### Temporary instance usage in ease.js Source: https://www.gnu.org/software/easejs/manual/easejs.html Shows how to create temporary instances in ease.js by calling the class as a function, enabling method chaining and callbacks without storing the instance. Each method must return itself for chaining to work. ```javascript // retrieve the name from an instance of Foo var name = Foo().getName(); // method chaining var car = VehicleFactory() .createBody() .addWheel( 4 ) .addDoor( 2 ) .build(); // temporary class with callback HttpRequest( host, port ).get( path, function( data ) { console.log( data ); } ); // Conventionally (without ease.js), you'd accomplish the above using // the 'new' keyword. You may still do this with ease.js, though it is // less clean looking. ( new Foo() ).someMethod(); ``` -------------------------------- ### Building an interface around XMLHttpRequest with Class.isA Source: https://www.gnu.org/software/easejs/manual/easejs.html Defines an HttpRequest interface modeled after XMLHttpRequest and uses it in a class constructor to enforce type checking. The example throws a TypeError if the passed object is not compatible, and demonstrates a successful instantiation with a real XMLHttpRequest. ```javascript // modeled around XMLHttpRequest var HttpRequest = Interface( { abort: [], open: [ 'method', 'url', 'async', 'user', 'password' ], send: [], } ); var FooApi = Class( { __construct: function( httpreq ) { if ( !( Class.isA( HttpRequest, httpreq ) ) ) { throw TypeError( "Expecting HttpRequest" ); } // ... } } ); FooApi( new XMLHttpRequest() ); // okay ``` -------------------------------- ### Accessing private member outside class throws TypeError Source: https://www.gnu.org/software/easejs/manual/easejs.html Attempting to access a private member from outside the class results in a TypeError. The example shows calling _moveFrontLeg on a Dog instance, which fails because private members are only available to the defining class. ```javascript var dog = Dog(); dog._moveFrontLeg( 1 ); // TypeError: Object # has no method '_moveFrontLeg' ``` -------------------------------- ### Using 'proxy' keyword to proxy methods to a member object Source: https://www.gnu.org/software/easejs/manual/easejs.html Defines a Pidgin class and an IratePidginCheep class that proxies 'cheep' and 'rename' methods to its '_pidgin' member. Shows method chaining and proxied calls with output. ```javascript var Pidgin = Class( 'Pidgin', { 'private _name': 'Flutter', 'public cheep': function( chirp ) { return this._name + ": cheep " + chirp; } 'public rename': function( name ) { this._name = ''+name; return this; } } ); var IratePidginCheep = Class( 'IratePidginCheep', { 'private _pidgin': null, __construct: function( pidgin ) { this._pidgin = pidgin; } // add our own method 'public irateCheep': function( chirp ) { return this._pidgin.cheep( chirp ).toUpperCase(); }, // retain original methods 'proxy cheep': '_pidgin', 'proxy rename': '_pidgin', } ); var irate = IratePidginCheep( Pidgin() ); irate.cheep( 'chirp' ); // "Flutter: cheep chirp" irate.setName( 'Butter' ).cheep( 'chirp' ); // "Butter: cheep chirp" irate.irateCheep( 'chop' ); // "BUTTER: CHEEP CHOP" ``` -------------------------------- ### Attempting to override private member in subclass Source: https://www.gnu.org/software/easejs/manual/easejs.html Defining a private method with the same name as a parent's private method does not override it; it creates a new method. The example shows a TwoLeggedDog class extending Dog with a private _moveFrontLeg that does nothing, but the parent's method is still called when walking. ```javascript var two_legged_dog = Class( 'TwoLeggedDog' ).extend( Dog, { /** * This won't override the parent method. */ 'private _moveFrontLeg': function( leg ) { // don't do anything return; }, } )(); two_legged_dog.walk(); ``` -------------------------------- ### Instantiate ease.js Class with or without new Source: https://www.gnu.org/software/easejs/manual/easejs.html Shows that constructors generated by ease.js can be called with or without the new keyword; both calls are equivalent. ease.js convention is to omit the keyword for concise, chainable code, but project conventions should be followed. ```javascript var Foo = Class( { /*...*/ } ); // both of these are equivalent Foo(); new Foo(); ``` -------------------------------- ### Private Method Implementation Using a Separate Prototype Source: https://www.gnu.org/software/easejs/manual/easejs.html Shows a possible private method implementation in JavaScript by defining private methods in a separate object and invoking them with call(). This approach avoids redefining methods per instance but reduces code clarity. The example includes a Stack class with push and pop methods. ```javascript var Stack = ( function() { // implementation of getSomeRandomName() is left up to the reader var _privname = getSomeRandomName(); var S = function() { // define a non-enumerable property to store our private data (will // only work in ES5+ environments) Object.defineProperty( this, _privname, { // ... (see previous example) } ); }; // private methods that only we will have access to var priv_methods = { getStack: function() { // note that, in order for 'this' to be bound to our instance, // it must be passed as first argument to call() or apply() return this[ _privname ].stack; }, }; // public methods S.prototype = { push: function( val ) { var stack = priv_methods.getStack.call( this ); stack.push( val ); }, pop: function() { var stack = priv_methods.getStack.call( this ); return stack.pop(); }, }; return S; } ); var inst = new Stack(); inst.push( 'foo' ); inst.pop(); // foo ``` -------------------------------- ### Using a random, non-enumerable property name to store private members Source: https://www.gnu.org/software/easejs/manual/easejs.html Defines a Stack constructor that stores private data in a non-enumerable property with a randomized name. Requires ECMAScript 5+ for Object.defineProperty. The property is non-enumerable, non-writable, and non-configurable, making it difficult to access. The example demonstrates push and pop operations on the private stack. ```javascript var Stack = ( function() { // implementation of getSomeRandomName() is left up to the reader var _privname = getSomeRandomName(); var S = function() { // define a non-enumerable property to store our private data (will // only work in ES5+ environments) Object.defineProperty( this, _privname, { enumerable: false, writable: false, configurable: false, value: { stack: [] } } ); }; S.prototype = { push: function( val ) { this[ _privname ].stack.push( val ); }, pop: function() { return this[ _privname ].stack.pop(); }, }; return S; } ); var inst = new Stack(); inst.push( 'foo' ); inst.pop(); // foo ``` -------------------------------- ### Why private member swapping is necessary (Figure B.14) Source: https://www.gnu.org/software/easejs/manual/easejs.html Shows a class C1 with a private member _name and a public method getName, and a subtype C0 that does not override getName. Without swapping the private object, _name would be undefined when invoked on C0. The example outputs "Foo" for both C1().getName() and C0().getName(). ```javascript var C1 = Class( { 'private _name': 'Foo', 'public getName': function() { return this._name; }, // ... } ), // note the naming convention using descending ids for the discussion // following this example C0 = C1.extend( { // ... } ); C1().getName(); // "Foo" C0().getName(); // "Foo" ``` -------------------------------- ### Vanilla ECMAScript object interface compatibility check with Class.isA Source: https://www.gnu.org/software/easejs/manual/easejs.html Shows how Class.isA checks a vanilla ECMAScript object against an ease.js interface. The object must define all interface members and meet parameter count requirements. The example includes false cases (missing member, insufficient parameters) and true cases (correct parameters, prototype-based object). ```javascript var Duck = Interface( { quack: [ 'str' ], waddle: [], } ); // false; no quack Class.isA( Duck, { waddle: function() {} } ); // false; quack requires one parameter Class.isA( Duck, { quack: function() {}, waddle: function() {}, } ); // true Class.isA( Duck, { quack: function( str ) {}, waddle: function() {}, } ); // true function ADuck() {}; ADuck.prototype = { quack: function( str ) {}, waddle: function() {}, }; Class.isA( Duck, ( new ADuck() ) ); ``` -------------------------------- ### Using Class.isInstanceOf and Class.isA to check instance types Source: https://www.gnu.org/software/easejs/manual/easejs.html Shows how to use ease.js's Class.isInstanceOf and Class.isA methods to check if an instance is of a certain type. Note that an instance must be passed as the second argument, not a class. ```javascript var dog = Dog() lazy = LazyDog(), angry = AngryDog(); Class.isInstanceOf( Dog, dog ); // true Class.isA( Dog, dog ); // true Class.isA( LazyDog, dog ); // false Class.isA( Dog, lazy ); // true Class.isA( Dog, angry ); // true // we must check an instance Class.isA( Dog, LazyDog ); // false; instance expected, class given ```