By Feng WeiGuo / forxm@21cn.com
2006-6
ESOE = ECMAScript OO Engine
This specification defines a method of object-oriented programming with ECMAScript 3rd compatible language.
Package: Objects to store name space structure.
Constructor: Function object
Class: Constructor.prototype
Declare: Let constructor create its class
Normalize: Load a constructor/object to a name space item
Import: Declare and Normalize.
Derive: Derive a class from a base class
Include: Load library file or class file.
|
Feature
|
Description
|
lever 1 (lv1)
|
1. Package definition
2. Class definition
3. Class deriving
4. Normalize non-esoe objects
|
1. Support script language compatible with ECMA-262 3rd
2. Manually load package.
3. An application's release version can use lv1 for efficiency.
|
lever 2 (lv2)
|
1. Automatically include package
|
1. Some script language may be unable to support this level.
|
$esoe
|
ESOE engine it self
|
_property
|
Prefix "_" to a property, except the "$esoe.ns".
|
_keyword()
|
Prefix "_" to keyword function that has a global version (c2), keyword is in lowercase.
|
$esoe.keyword()
|
Keyword function that only in $esoe (c4) are lowercase.
|
$esoe.MyFunc()
|
To other function in $esoe, the first letter of each word is uppercase.
|
func
|
abbr. function/constructor
|
pkg
|
abbr. package
|
name
|
a String object
|
Keywords conflicting
Type
|
Keywords
|
Description
|
c1
|
$esoe
prototype._esoe
[object]._esoe
|
If any c1 keywords conflict, ESOE engine will fail.
|
c2
|
_package / $esoe._package
_normalize / $esoe._normalize
_import / $esoe._import
_derive / $esoe._derive
_packagepath / $esoe._packagepath (lv2)
_update / $esoe._update (lv2)
|
If any c2 keywords conflict, close those keywords with the "nokey" parameter, and use the $esoe.* version.
So in the release version, it's better to convert all c2 keywords to their $esoe.* version.
|
c3
|
(prototype.)_base / (prototype.)_esoe._base
(prototype.)_pkg / (prototype.)_esoe._ pkg
|
c3 keywords are for normalizing non-esoe object. If any c3 keywords conflict with a non-esoe object, close this object's all c3 keywords when it's normalized, and use the _esoe.* version.
|
c4
|
$esoe.normalizeobject
[func]._declare / [func]._esoe._declare
|
c4 keywords will not conflict
|
Keywords description
Keyword
|
Description
|
$esoe
|
=GLOBAL.$esoe, the esoe engine itself
|
prototype._esoe / [object]._esoe
|
To maintain classes/objects
|
_package
|
Pre-define a package
|
_normalize
|
Load a constructor to a name space item
|
_import
|
Let constructor create it's class and map it to a name space
|
_derive
|
Derive a class from a base class
|
_packagepath (lv2)
|
Set the package path
|
_update (lv2)
|
If package loading need recursive process, call this to active it.
|
(prototype.)_base
|
Base class, that is, base class constructor's prototype
|
(prototype.)_pkg
|
The nearest package of a class
|
$esoe.normalizeobject
|
Normalize a non-esoe object
|
[func]._declare
|
Let constructor create it's class
|
ESOE layout include 3 parts,
1. Load ESOE engine, load parameters.
2. If any libraries are required, load them. It's optional for lv2.
3. If any libraries and classes are required, initialize all of them.
NOTE: ESOE layout is strongly required to be independent from other user codes, although it's not necessary in some environment.
Example for javasript in html page:
......
<script src="../esoe.js" level="2" debug="1"></script>
//load ESOE engine
<script src="../net/eae/webfx/xtree/xtree.js"></script>
//load libraries
<script>
//initialize packages and classes
_packagepath("com","../");
_import("net.eae.webfx.xtree.WebFXTree");
_import("net.eae.webfx.xtree.WebFXTreeItem");
_import("com.jsvm.js.lang.StringBuffer" , "myapp.StringBuffer");
_update ();
</script>
//ESOE layout end here
<script>......</script>
//separated from other user code
......
<script>
//user code
......
var obTree= new WebFXTree('name1');
var sb= new myapp.StringBuffer;
......
</script >
Create a window.$esoe as an Object object.
It has below property that can be load from external.
_param
|
Save all of the parameter for later usage.
|
_path
|
ESOE engine path
|
_level
|
Level parameter, 1(default) or 2
|
_debug
|
Debug parameter, 0(default) or 1 in current version
|
_nokey
|
c2 keyword list to be forbidden
|
_libpath (lv2)
|
Default library path, only one path can be set. If not set, use _path as _libpath
|
_libmode (lv2)
|
0: do not automatically load library or class file (default).
1: load package library firstly, then class file.
2: load class file firstly, then package library.
3: load package library only
4: load class file only
|
It has below internal property,
ns
|
Name space map
|
_lib (lv2)
|
Name space map for library including
|
_lib.$count (lv2)
|
Set the number of files that included
|
Each of name space item of $esoe._lib has below property,
[item].$path (lv2)
|
Save package path set by _packagepath().
|
[item].$map (lv2)
|
Save the 2nd name space set by _import().
|
[item].$inc (lv2)
|
Set the number of times that a class/library has included.
|
[item].$fail (lv2)
|
Save the name space string if a class is not exist.
|
In ESOE, a package is represented as name space chain. That is, a chain of objects under the $esoe.ns object, such as "com.mysite.myclass" is mapped to $esoe.ns.com.mysite.myclass. Every item in the chain is an Object object, or be other kind of object that can add property, such as a Function object, an Array object, etc. On the other side, Number object, String object, Boolean, undefined, null, etc., can not be a name space item, because they can not add any property.
Normalizing a constructor will firstly check that if the global name space had been prepared, else it will call _package() to create the package name space, and create a reference to the constructor. When create the reference, it’s important to check the name space confliction. If name space item has existed and it’s not the constructor itself, the normalizing process fail and return false. If $esoe._debug=1, it will alert an error message before return.
If the mapping success, it will add below property to the constructor’s prototype
prototype._esoe
|
An Object object to save the information for ESOE engine.
|
The prototype._esoe have below properties
_esoe._base
|
Record the constructor’s prototype of its base class.
|
_esoe._pkg
|
Record the nearest upper package namespace item of the class
|
_esoe._conflict
|
set to 1 if this class has any c3 keyword conflict
|
_esoe._nonesoe
|
set to 1 if this item is a non-esoe class/object
|
More, normalizing will do as below, like those system objects do in ECMAScript,
func.prototype.constructor= func
if _esoe._conflict=0, normalizing will do as below
func.prototype._base= func.prototype._esoe._base;
func.prototype._pkg= func.prototype._esoe._pkg;
these two keywords are for the convenience for the users.
In ESOE, every Function object with its prototype prepared ( that is, the prototype will not be changed later) can be normalized. So just manually call _normalize() to load a non-esoe function to as a name space item. For an ESOE style class, ESOE will automatically do this work.
Normalizing a non-esoe function will not set the prototype.constructor and _esoe._base property.
For some existed library system, it may have used global variables. To rapidly transform these systems to be compatible with ESOE engine, global variables can be map into the name space also. It does just like normalizing a non-esoe function. The _esoe property is added to the object instead of prototype.
Normalizing non-esoe object is not including Number/String/Boolean/null, etc..
In ESOE, class is represented as a constructor's prototype.
The constructor of every class should add a _declare property. The _declare property is a Function object with no argument. In the _declare function, the prototype can be created and/or initialized. The _declare() function is actually a callback function for the ESOE engine.
When user _import() a class, ESOE engine will call a constructor's _declare(), at this time, the constructor's prototype have not been a reference of any instance, for the [new constructor] statement has not been used to create the first instance of the class/constructor.
If the constructor has "_declare" keyword conflict, instead of creating _declare(), but create a _esoe._declare().ESOE engine will automatically select which to use.
For ESOE engine, defined a class as below,
1. Create a Function object. An empty prototype object is also created by ECMAScript engine.
2. If Function object do not have a "_declare" keyword conflict, add a _declare() property to the Function object. Otherwise, add a _esoe Object object as property of the Function object, then add a _declare() to the _esoe.
3. In the _declare(), created and/or initialized the prototype. The default prototype can be replaced in _declare().
If the _declare() need to import some class, try to import the class to a variable before deriving and normalizing, like below,
var ns={};
if( !_import("MyPkg.Class5","",ns) ) return false;
…
if( !_derive(…) ) return false;
…
var a= new ns.Class5;
If the method of a class need to import another class, do it just like that in _declare(). But if the imported class is in the same package, it’s easier to do so,
var a=new this._pkg.Class5;
If a class is derived from a base class, let the prototype be an instance of its base class.
[class].prototype= new [baseclass];
In the _declare() callback function, call _derive([thisclass],[baseclass]) instead of directly create the prototype.
For lv1, all base classes should have been included before deriving a class from them. Else the class will fail.
For lv2, if a base class is not imported, _derive() return false. When _derive() return false, the caller [func]._declare() should return false.
1. Prepare package namespace.
2. Create constructor and initializing process for the instance.
3. Create constructor’s _declare() process.
3.1 Import external classes
3.2 Call _normalize() for class without base class, or call _derive().
3.3 Add class property and method to the prototype of the class.
3.4 If _declare success, return true, else return false.
Example:
_package("MyPkg.sub");
// Prepare package namespace
$esoe.ns.MyPkg.sub.Class3= function(){}
// Create constructor
$esoe.ns.MyPkg.sub.Class3._declare= function()
// Create constructor’s _declare()
{
var ns={}; // Import external classes
if( !_import("MyPkg.Class5","Class5",ns) ) return false;
if( !_derive("MyPkg.sub.Class3","MyPkg.Class2") ) //derive or normalize
return false;
with($esoe.ns.MyPkg.sub.Class3) //add property to the prototype
{
prototype.p1= new ns.Class5;
prototype.func1= function() {}
}
return true; //return
}
Class Importing is like class normalizing, but the class is not mapped to $esoe.ns, it will map to a user appointed place. Also, the conflict checking is important.
For lv2, to deploy a package to a physical file path, just put the class file to the directory like that in name space chain, but replace the "." with "/", such as .../com/mysite/myclass.[file extension]. Especially, for ESOE lv2, if all classes of a package have packed together to a single file, name this file as .../com/mysite/_all.[file extension], then ESOE engine will also load this library file.
A package's path is saved to $esoe._lib.[item].$path. When retrieve the package name, the $path will append current package's name. ESOE engine will search the nearest package path if multiple package paths are set. For example,
if set
_packagepath("com","../lib/" );
_packagepath("com.mylib","../lib2/" );
then
"com.xtree.WebFXTree" will be loaded from ../lib/com/xtree/WebFXTree
"com.mylib.class1" will be loaded from ../lib2/ mylib/class1
If ESOE engine find that a constructor do not exist, it will set the corresponding [item].$fail and [item].$map of $esoe._lib. It will check [item].$inc of $esoe._lib and $esoe._libmode and the upper package's [item].$inc to see if it's needed to load the constructor. If it load the file, it will set [item].$inc and $esoe._lib.$count.
If the including requires recursive process, call _update() at the last place to active the recursive mechanism.