xObject
A lightweight hookable factory providing control over object instantiation.
Grounds
As a developer I want to follow 'Constructor that returns Object Literal' pattern while declaring objects as in my opinion it is the best maintainable design available for JavaScript (prior to ES Harmony). However that structure doesn't allow any easy way to inherit. As soon as I realised that I can get control over object instantiation via a factory and thus implement a custom inheritance I came up with this library. It was firstly released in 2010 as JSA. Recently the library was refactored and the factory API is changed. Since the factory makes an alternative to Object.create method, it was named xObject.create.
Features
- Core: Class-based descending inheritance (abstract class -> class -> .. -> final class)
- Mixin hook: Multiple inheritance by using mixins
- Interface hook: Interface annotation and run-time validation
- DbC hook: Design by Contract practices: entry/exit point validators
- Widget hook: YUI-like Widget foundation class
Exampes
Class-based descending inheritance
var { return foo: "value" ; } { // Constructor's job var _privateMember = "private member"; return __extends__: AbstractClass publicMember : "public member" { return _privateMember; } ; }; var obj = xObject; assert; assert;
Passing arguments to the constructor
var { var _arg1 = arg1 _arg2 = arg2; return { return _arg1; } { return _arg2; } ;}; var obj = xObject;assert;assert;
Using constructor pseudo-method
var { return { thisarg1 = arg1; thisarg2 = arg2; } ; }; var obj = xObject; assert; assert;
Importing properties in Object.create way
var {};var obj = xObject;// or xObject.create( ConcreteClass, [], { foo : 'foo' } );assert;
Using mixin pseudo-property
var MixinA = propertyA: "propertyA";MixinB = propertyB: "propertyB"; { return __mixin__: MixinA MixinB ownPropery: "Own property" }; var obj = xObject;assert;assert;assert;
Using implements pseudo-property
var { return ;} ConcreteInterface = requeriedMethod : "string" InjectedDependency { return __implements__: ConcreteInterface { } }; var dependency = xObject module = ; assert;module; // OKmodule; // throws a TypeError exceptionmodule; // throws a TypeError exception
Design by Contract
var ConcreteContract = aMethod : onEntry: "number" validators: { return arg > 10; } onExit: "string" { return __contract__: ConcreteContract { return "a string"; } };var module = EmployedModule;module; // OKmodule; // validator fails, RangeError exception is thrown
Extending WidgetAbstract
YUI provides a sophisticated solution for keeping Widget objects consistent. xObject borrowed the concepts of base xObject.WidgetAbstract type from which all the Widget objects derived.
A Widget object extending xObject.WidgetAbstract may any of following members:
- HTML_PARSER - object literal mapping this.node properties to supplied selectors. E.g { title: "#title" } obtains reference to a node of id "title" (in the context of boundingBox) and exposes it in this.node.title.
- renderUi - method responsible for creating and adding the nodes which the widget needs into the document
- bindUi - method responsible for attaching event listeners which bind the UI to the widget state.
- syncUI - method responsible for setting the initial state of the UI based on the current state of the widget at the time of rendering.
When xObject.create instantiates a derivative of xObject.WidgetAbstract it populates node property with node references given in HTML_PARSER and call init, renderUi, bindUi and syncUi methods when any available.
{ "use strict"; // Concrete widget { return __extends__ : jsaWidgetAbstract HTML_PARSER : toolbar : 'div.toolbar' { thisnodetoolbar; } { thisnodeboundingBox; } ; }; // Document is ready ; } jQuery xObject ;
Note: AbstractWidget obtain nodes from DOM by supplied selector strings by using xObject.querySelectorFn( selector[, context] ) function, which you can override. When it's not overridden, by default it will rely on VanillaJS querySelector method, but if jQuery available in the global scope it will switch to $().