
1.0 Introduction

1.1 Preface (IMPORTANT)
WARNING. This is for verision 2.x of the library, which introduces major syntax differences for when accessing instance variables and functions, and static variables and functions, from within static functions. For the old documentation, refer to V1.x Documentation
CrxOop is a javascript (JS) library whose primary aim is to allow JS developers to develop using interfaces and classes as they are known in other object oriented programming (OOP) languages such as C++, C#, Java and PHP. The aim of this documentation is to describe the available features and their syntax. The documentation does not aim to teach you OOP, nor does it aim to teach you JS. Further more, I shall rely mostly on C++ terminology and syntax throughout this documentation, with some exceptions such as the word 'interface' which is borrowed from Java. This does not mean that this terminology is exclusive to those languages, but I am simply ensuring my communication is defined. The code presented here, especially that which is written in C++, does not constitute best practice and should not be used as reference in any way beyond what is intended here.
This documentation is very brief about the actual details of OOP concepts. For further details, refer to C++'s documentation first, and if the topic is not found there, Java's documentation. A web search engine can be very handy. Needless to say, familiarity with any of C++, Java, and to some extent C# and PHP, is very useful when learning CrxOop.
Version 1.4 of the library introduces Structures, a formalization and generalization of prototypal inheritance in javascript. Structures are meant primarly to be used for data structures, but they can be used by those who prefer prototypal inheritance for certain algorithms, taking advantage of well defined and enforced accessors, well defined constructor invocation, and data typing. In other words, structures are CrxOop's implementation of Prototype Based Programming, or as this author likes to call it, Prototype Object Based Programming (POBP).

1.2 Getting Started
You can download the library from: DOWNLOAD LINK
If you wish to use the library from a browser, simply include the code in your header, and you can begin using the library.
If you wish to use the library from Node.js, please refer to the section on Node.js usage.
The code is released under the MIT license. Please refer to the end of this documentation for full details.
If you wish to report an error, please visit my github page at github.

1.3 OOP (Classes, Interfaces) Syntax Overview
CrxOop's OOP facility offers one syntax for defining interfaces, two syntaxes for defining classes, and one syntax for everything else. The following is C++ code along with the two different equivalent syntaxes in JS that are provided by CrxOop. Apart from constants, the illustration should cover quickly most of the features supported. The last two tabs are useful for comparing between the C++ code and the JS code.
#include <iostream>
using namespace std;
class InterfaceA
{
public: virtual void interfaceAFunction(int pA) = 0;
};
class InterfaceB
{
public: virtual void interfaceBFunction(int pA) = 0;
};
class InterfaceC : public InterfaceA, public InterfaceB
{
public: virtual void interfaceCFunction1(int pA) = 0;
public: virtual void interfaceCFunction2(int pA) = 0;
};
class InterfaceD
{
public: virtual void interfaceDFunction(int pA) = 0;
};
class classA
{
friend class classD;
public: classA()
{cout << "CONSTRUCTING A\n";}
public: char publicVar[19] = "classA::publicVar\n";
public: int publicVar2 = 5;
public: int publicVar3[5] = {0, 0, 0, 0, 0};
public: static char publicStaticVar[25];
public: static void publicStaticFunction(classA* pClassA)
{
cout << "[START]classA::publicStaticFunction()\n";
classA::privateStaticFunction(5);
cout << classA::publicStaticVar;
cout << classA::privateStaticVar;
cout << pClassA->privateVar;
cout << "[END]classA::publicStaticFunction()\n";
}
public: void publicFunction(int pA)
{
cout << "classA::publicFunction()\n";
}
public: classA* test(int pA)
{
cout << "[START]classA::test()\n";
this->publicVirtualFunction(5);
this->privateVirtualFunction(5);
this->publicPureVirtualFunction(5);
this->privatePureVirtualFunction(5);
this->protectedPureVirtualFunction(5);
this->publicFunction(5);
this->privateFunction(5);
classA::privateStaticFunction(5);
cout << classA::publicStaticVar;
cout << classA::privateStaticVar;
cout << "[END]classA::test()\n";
return this;
}
public: virtual void publicVirtualFunction(int pA)
{
cout << "classA::publicVirtualFunction()\n";
}
public: virtual void publicPureVirtualFunction(int pA) = 0;
private: char privateVar[20] = "classA::privateVar\n";
private: static char privateStaticVar[26];
private: static void privateStaticFunction(int pA)
{
cout << "classA::privateStaticFunction()\n";
}
private: void privateFunction(int pA)
{
cout << "classA::privateFunction()\n";
}
private: virtual void privateVirtualFunction(int pA)
{
cout << "classA::privateVirtualFunction()\n";
}
private: virtual void privatePureVirtualFunction(int pA) = 0;
protected: void protectedFunction(int pA)
{
cout << "classA::protectedFunction()\n";
}
protected: virtual void protectedVirtualFunction(int pA)
{
cout << "classA::protectedVirtualFunction()\n";
}
protected: virtual void protectedPureVirtualFunction(int pA) = 0;
};
char classA::publicStaticVar[25] = "classA::publicStaticVar\n";
char classA::privateStaticVar[26] = "classA::privateStaticVar\n";
class classB: public classA, public InterfaceC, public InterfaceD
{
public: classB(int pA)
{cout << "CONSTRUCTING B\n";}
public: char publicVar[19] = "classB::publicVar\n";
public: void publicFunction(int pA)
{
cout << "classB::publicFunction()\n";
if(pA != 1)
{this->publicFunction(1);}
this->classA::publicFunction(5);
}
public: classB* test(int pA)
{
cout << "[START]classB::test()\n";
this->publicVirtualFunction(5);
this->publicFunction(5);
((classA*)this)->publicFunction(5);
cout << "[END]classB::test()\n";
return this;
}
public: virtual void publicVirtualFunction(int pA)
{
cout << "classB::publicVirtualFunction()\n";
this->classA::publicVirtualFunction(pA);
}
public: virtual void interfaceAFunction(int pA)
{}
public: virtual void interfaceBFunction(int pA)
{}
public: virtual void interfaceCFunction1(int pA)
{}
public: virtual void interfaceCFunction2(int pA)
{}
public: virtual void interfaceDFunction(int pA)
{}
};
class classC: public classB
{
public: classC():classB(5)
{
cout << "CONSTRUCTING C\n";
}
public: virtual void publicVirtualFunction(int pA) final
{
cout << "classC::publicVirtualFunction()\n";
this->classA::publicVirtualFunction(pA);
this->classA::publicFunction(pA);
this->classA::publicVar2 = 6;
this->classA::publicVar3[0] = 1;
cout << this->classA::publicVar3[0] << "\n";
}
public: virtual void publicPureVirtualFunction(int pA)
{
cout << "classC::publicPureVirtualFunction()\n";
}
private: virtual void privatePureVirtualFunction(int pA)
{
cout << "classC::privatePureVirtualFunction()\n";
}
protected: virtual void protectedPureVirtualFunction(int pA)
{
cout << "classC::protectedPureVirtualFunction()\n";
}
};
class classD
{
public: void test(classA* pClassA)
{
cout << "[START]classD::test()\n";
cout << pClassA->privateVar;
pClassA->privatePureVirtualFunction(5);
pClassA->protectedFunction(5);
cout << "[END]classD::test()\n";
}
};
int main()
{
classC* a = new classC();
classD* b = new classD();
a->test(5);
((classA*)a)->test(5);
classA::publicStaticFunction((classA*)a);
b->test((classA*)a);
return 0;
}
CONSTRUCTING B
CONSTRUCTING C
[START]classB::test()
classC::publicVirtualFunction()
classA::publicVirtualFunction()
classA::publicFunction()
1
classB::publicFunction()
classB::publicFunction()
classA::publicFunction()
classA::publicFunction()
classA::publicFunction()
[END]classB::test()
[START]classA::test()
classC::publicVirtualFunction()
classA::publicVirtualFunction()
classA::publicFunction()
1
classA::privateVirtualFunction()
classC::publicPureVirtualFunction()
classC::privatePureVirtualFunction()
classC::protectedPureVirtualFunction()
classA::publicFunction()
classA::privateFunction()
classA::privateStaticFunction()
classA::publicStaticVar
classA::privateStaticVar
[END]classA::test()
[START]classA::publicStaticFunction()
classA::privateStaticFunction()
classA::publicStaticVar
classA::privateStaticVar
classA::privateVar
[END]classA::publicStaticFunction()
[START]classD::test()
classA::privateVar
classC::privatePureVirtualFunction()
classA::protectedFunction()
[END]classD::test()
crx_registerInterface("InterfaceA",
{
"interfaceAFunction": 0
});
crx_registerInterface("InterfaceB",
{
"interfaceBFunction": 0
});
crx_registerInterface("InterfaceC",
{
INHERITS: ["InterfaceA", "InterfaceB"],
"interfaceCFunction1": 0,
"interfaceCFunction2": 0
});
crx_registerInterface("InterfaceD",
{
"interfaceDFunction": 0
});
crx_registerClass("classA",
{
FRIENDS: ['classD'],
PUBLIC:
{
CONSTRUCT: function()
{console.log("CONSTRUCTING A");},
VARS:
{
"publicVar": "classA::publicVar",
"publicVar2": 5,
"publicVar3": [0, 0, 0, 0, 0]
},
STATIC:
{
VARS:
{
"publicStaticVar": "classA::publicStaticVar"
},
FUNCTIONS:
{
"publicStaticFunction": function(pClassA)
{
console.log("[START]classA::publicStaticFunction()");
this.STATIC.privateStaticFunction(5);
console.log(crx_static("classA").publicStaticVar); //OR this.STATIC.publicStaticVar
console.log(this.STATIC.privateStaticVar);
console.log(this.O(pClassA).privateVar);
console.log("[END]classA::publicStaticFunction()");
}
}
},
FUNCTIONS:
{
"publicFunction": function(pA)
{
console.log("classA::publicFunction()");
},
"test": function(pA)
{
console.log("[START]classA::test()");
this.publicVirtualFunction(5);
this.privateVirtualFunction(5);
this.publicPureVirtualFunction(5);
this.privatePureVirtualFunction(5);
this.protectedPureVirtualFunction(5);
this.publicFunction(5);
this.privateFunction(5);
this.STATIC.privateStaticFunction(5);
console.log(crx_static("classA").publicStaticVar);
console.log(this.STATIC.privateStaticVar);
console.log("[END]classA::test()");
return this.THIS;
}
},
VIRTUAL:
{
FUNCTIONS:
{
"publicVirtualFunction": function(pA)
{
console.log("classA::publicVirtualFunction()");
},
"publicPureVirtualFunction": 0
}
}
},
PRIVATE:
{
VARS:
{
"privateVar": "classA::privateVar"
},
STATIC:
{
VARS:
{
"privateStaticVar": "classA::privateStaticVar"
},
FUNCTIONS:
{
"privateStaticFunction": function(pA)
{
console.log("classA::privateStaticFunction()");
}
}
},
FUNCTIONS:
{
"privateFunction": function(pA)
{
console.log("classA::privateFunction()");
}
},
VIRTUAL:
{
FUNCTIONS:
{
"privateVirtualFunction": function(pA)
{
console.log("classA::privateVirtualFunction()");
},
"privatePureVirtualFunction": 0
}
}
},
PROTECTED:
{
FUNCTIONS:
{
"protectedFunction": function(pA)
{
console.log("classA::protectedFunction()");
}
},
VIRTUAL:
{
FUNCTIONS:
{
"protectedVirtualFunction": function(pA)
{
console.log("classA::protectedVirtualFunction()");
},
"protectedPureVirtualFunction": 0
}
}
}
});
crx_registerClass("classB",
{
IMPLEMENTS: ["InterfaceC", "InterfaceD"],
EXTENDS: "classA",
PUBLIC:
{
CONSTRUCT: function(pA)
{console.log("CONSTRUCTING B");},
VARS:
{
"publicVar": "classB::publicVar"
},
FUNCTIONS:
{
"publicFunction": function(pA)
{
console.log("classB::publicFunction()");
if(pA != 1)
{this.publicFunction(1);}
this.PARENT.publicFunction(5);
},
"test": function(pA)
{
console.log("[START]classB::test()");
this.publicVirtualFunction(5);
this.publicFunction(5);
this.CAST("classA").publicFunction(5);
console.log("[END]classB::test()");
return this.THIS;
}
},
VIRTUAL:
{
FUNCTIONS:
{
"publicVirtualFunction": function(pA)
{
console.log("classB::publicVirtualFunction()");
this.SR(null, "publicVirtualFunction", pA);
},
"interfaceAFunction": function(pA)
{},
"interfaceBFunction": function(pA)
{},
"interfaceCFunction1": function(pA)
{},
"interfaceCFunction2": function(pA)
{},
"interfaceDFunction": function(pA)
{}
}
}
}
});
crx_registerClass("classC",
{
EXTENDS: "classB",
PUBLIC:
{
CONSTRUCT: function()
{
this.PARENT.CONSTRUCT(5);
console.log("CONSTRUCTING C");
},
VIRTUAL:
{
FINAL:
{
FUNCTIONS:
{
"publicVirtualFunction": function(pA)
{
console.log("classC::publicVirtualFunction()");
this.SR("classA", "publicVirtualFunction", pA);
this.SR("classA", "publicFunction", pA);
this.SR("classA", "publicVar2", 6);
this.SR("classA", "publicVar3")[0] = 1;
console.log(this.SR("classA", "publicVar3")[0]);
}
}
},
FUNCTIONS:
{
"publicPureVirtualFunction": function(pA)
{
console.log("classC::publicPureVirtualFunction()\n");
}
}
}
},
PRIVATE:
{
VIRTUAL:
{
FUNCTIONS:
{
"privatePureVirtualFunction": function(pA)
{
console.log("classC::privatePureVirtualFunction()");
}
}
}
},
PROTECTED:
{
VIRTUAL:
{
FUNCTIONS:
{
"protectedPureVirtualFunction": function(pA)
{
console.log("classC::protectedPureVirtualFunction()");
}
}
}
}
});
crx_registerClass('classD',
{
PUBLIC:
{
FUNCTIONS:
{
"test": function(pClassA)
{
console.log("[START]classD::test()");
console.log(this.O(pClassA, "classA").privateVar);
this.O(pClassA, "classA").privatePureVirtualFunction(5);
this.O(pClassA, "classA").protectedFunction(5);
console.log("[END]classD::test()");
}
}
}
});
var a = crx_new("classC");
var b = crx_new("classD");
a.test(5);
a.CAST("classA").test(5);
crx_static("classA").publicStaticFunction(a.CAST("classA"));
b.test(a.CAST("classA"));
CONSTRUCTING A
CONSTRUCTING C
[START]classB::test()
classC::publicVirtualFunction()
classA::publicVirtualFunction()
classA::publicFunction()
1
classB::publicFunction()
classB::publicFunction()
classA::publicFunction()
classA::publicFunction()
classA::publicFunction()
[END]classB::test()
[START]classA::test()
classC::publicVirtualFunction()
classA::publicVirtualFunction()
classA::publicFunction()
1
classA::privateVirtualFunction()
classC::publicPureVirtualFunction()
classC::privatePureVirtualFunction()
classC::protectedPureVirtualFunction()
classA::publicFunction()
classA::privateFunction()
classA::privateStaticFunction()
classA::publicStaticVar
classA::privateStaticVar
[END]classA::test()
[START]classA::publicStaticFunction()
classA::privateStaticFunction()
classA::publicStaticVar
classA::privateStaticVar
classA::privateVar
[END]classA::publicStaticFunction()
[START]classD::test()
classA::privateVar
classC::privatePureVirtualFunction()
classA::protectedFunction()
[END]classD::test()
crx_registerInterface("InterfaceA",
{
"interfaceAFunction": 0
});
crx_registerInterface("InterfaceB",
{
"interfaceBFunction": 0
});
crx_registerInterface("InterfaceC",
{
INHERITS: ["InterfaceA", "InterfaceB"],
"interfaceCFunction1": 0,
"interfaceCFunction2": 0
});
crx_registerInterface("InterfaceD",
{
"interfaceDFunction": 0
});
crx_registerClass("classA",
{
"VERBOSE": 1,
"friends": ["classD"],
"public CONSTRUCT": function()
{
console.log("CONSTRUCTING A");
},
"public var publicVar": "classA::publicVar",
"public var publicVar2": 5,
"public var publicVar3": [0, 0, 0, 0, 0],
"public static var publicStaticVar": "classA::publicStaticVar",
"public static function publicStaticFunction": function(pClassA)
{
console.log("[START]classA::publicStaticFunction()");
this.STATIC.privateStaticFunction(5);
console.log(crx_static("classA").publicStaticVar); //OR this.STATIC.publicStaticVar
console.log(this.STATIC.privateStaticVar);
console.log(this.O(pClassA).privateVar);
console.log("[END]classA::publicStaticFunction()");
},
"public function publicFunction": function(pA)
{
console.log("classA::publicFunction()");
},
"public function test": function(pA)
{
console.log("[START]classA::test()");
this.publicVirtualFunction(5);
this.privateVirtualFunction(5);
this.publicPureVirtualFunction(5);
this.privatePureVirtualFunction(5);
this.protectedPureVirtualFunction(5);
this.publicFunction(5);
this.privateFunction(5);
this.STATIC.privateStaticFunction(5);
console.log(this.STATIC.publicStaticVar);
console.log(this.STATIC.privateStaticVar);
console.log("[END]classA::test()");
return this.THIS;
},
"public virtual function publicVirtualFunction": function(pA)
{
console.log("classA::publicVirtualFunction()");
},
"public virtual function publicPureVirtualFunction": 0,
"private var privateVar": "classA::privateVar",
"private static var privateStaticVar": "classA::privateStaticVar",
"private static function privateStaticFunction": function(pA)
{
console.log("classA::privateStaticFunction()");
},
"private function privateFunction": function(pA)
{
console.log("classA::privateFunction()");
},
"private virtual function privateVirtualFunction": function(pA)
{
console.log("classA::privateVirtualFunction()");
},
"private virtual function privatePureVirtualFunction": 0,
"protected function protectedFunction": function(pA)
{
console.log("classA::protectedFunction()");
},
"protected virtual function protectedVirtualFunction": function(pA)
{
console.log("classA::protectedVirtualFunction()");
},
"protected virtual function protectedPureVirtualFunction": 0
});
crx_registerClass("classB",
{
"VERBOSE": 1,
"implements": ["InterfaceC", "InterfaceD"],
"extends": "classA",
"public CONSTRUCT": function(pA)
{
console.log("CONSTRUCTING B");
},
"public var publicVar": "classB::publicVar",
"public function publicFunction": function(pA)
{
console.log("classB::publicFunction()");
if(pA != 1)
{this.publicFunction(1);}
this.PARENT.publicFunction(5);
},
"public function test": function(pA)
{
console.log("[START]classB::test()");
this.publicVirtualFunction(5);
this.publicFunction(5);
this.CAST("classA").publicFunction(5);
console.log("[END]classB::test()");
return this.THIS;
},
"public virtual function publicVirtualFunction": function(pA)
{
console.log("classB::publicVirtualFunction()");
this.SR(null, "publicVirtualFunction", pA);
},
"public virtual function interfaceAFunction": function(pA)
{},
"public virtual function interfaceBFunction": function(pA)
{},
"public virtual function interfaceCFunction1": function(pA)
{},
"public virtual function interfaceCFunction2": function(pA)
{},
"public virtual function interfaceDFunction": function(pA)
{}
});
crx_registerClass("classC",
{
"VERBOSE": 1,
"extends": "classB",
"public CONSTRUCT": function()
{
this.PARENT.CONSTRUCT(5);
console.log("CONSTRUCTING C");
},
"public virtual function publicVirtualFunction": function(pA)
{
console.log("classC::publicVirtualFunction()");
this.SR("classA", "publicVirtualFunction", pA);
this.SR("classA", "publicFunction", pA);
this.SR("classA", "publicVar2", 6);
this.SR("classA", "publicVar3")[0] = 1;
console.log(this.SR("classA", "publicVar3")[0]);
},
"public virtual function publicPureVirtualFunction": function(pA)
{
console.log("classC::publicPureVirtualFunction()\n");
},
"private virtual function privatePureVirtualFunction": function(pA)
{
console.log("classC::privatePureVirtualFunction()");
},
"protected virtual function protectedPureVirtualFunction": function(pA)
{
console.log("classC::protectedPureVirtualFunction()");
}
});
crx_registerClass('classD',
{
"VERBOSE": 1,
"public function test": function(pClassA)
{
console.log("[START]classD::test()");
console.log(this.O(pClassA, "classA").privateVar);
this.O(pClassA, "classA").privatePureVirtualFunction(5);
this.O(pClassA, "classA").protectedFunction(5);
console.log("[END]classD::test()");
}
});
var a = crx_new("classC");
var b = crx_new("classD");
a.test(5);
a.CAST("classA").test(5);
crx_static("classA").publicStaticFunction(a.CAST("classA"));
b.test(a.CAST("classA"));
CONSTRUCTING A
CONSTRUCTING C
[START]classB::test()
classC::publicVirtualFunction()
classA::publicVirtualFunction()
classA::publicFunction()
1
classB::publicFunction()
classB::publicFunction()
classA::publicFunction()
classA::publicFunction()
classA::publicFunction()
[END]classB::test()
[START]classA::test()
classC::publicVirtualFunction()
classA::publicVirtualFunction()
classA::publicFunction()
1
classA::privateVirtualFunction()
classC::publicPureVirtualFunction()
classC::privatePureVirtualFunction()
classC::protectedPureVirtualFunction()
classA::publicFunction()
classA::privateFunction()
classA::privateStaticFunction()
classA::publicStaticVar
classA::privateStaticVar
[END]classA::test()
[START]classA::publicStaticFunction()
classA::privateStaticFunction()
classA::publicStaticVar
classA::privateStaticVar
classA::privateVar
[END]classA::publicStaticFunction()
[START]classD::test()
classA::privateVar
classC::privatePureVirtualFunction()
classA::protectedFunction()
[END]classD::test()
#include <iostream>
using namespace std;
class InterfaceA
{
public: virtual void interfaceAFunction(int pA) = 0;
};
crx_registerInterface("InterfaceA",
{
"interfaceAFunction": 0
});
class InterfaceB
{
public: virtual void interfaceBFunction(int pA) = 0;
};
crx_registerInterface("InterfaceB",
{
"interfaceBFunction": 0
});
class InterfaceC : public InterfaceA, public InterfaceB
{
public: virtual void interfaceCFunction1(int pA) = 0;
public: virtual void interfaceCFunction2(int pA) = 0;
};
crx_registerInterface("InterfaceC",
{
INHERITS: ["InterfaceA", "InterfaceB"],
"interfaceCFunction1": 0,
"interfaceCFunction2": 0
});
class InterfaceD
{
public: virtual void interfaceDFunction(int pA) = 0;
};
crx_registerInterface("InterfaceD",
{
"interfaceDFunction": 0
});
class classA
{
friend class classD;
crx_registerClass("classA",
{
FRIENDS: ['classD'],
PUBLIC:
{
public: classA()
{cout << "CONSTRUCTING A\n";}
CONSTRUCT: function()
{console.log("CONSTRUCTING A");},
public: char publicVar[19] = "classA::publicVar\n";
public: int publicVar2 = 5;
public: int publicVar3[5] = {0, 0, 0, 0, 0};
VARS:
{
"publicVar": "classA::publicVar",
"publicVar2": 5,
"publicVar3": [0, 0, 0, 0, 0]
},
STATIC:
{
public: static char publicStaticVar[25];
VARS:
{
"publicStaticVar": "classA::publicStaticVar"
},
public: static void publicStaticFunction(classA* pClassA)
{
cout << "[START]classA::publicStaticFunction()\n";
classA::privateStaticFunction(5);
cout << classA::publicStaticVar;
cout << classA::privateStaticVar;
cout << pClassA->privateVar;
cout << "[END]classA::publicStaticFunction()\n";
}
FUNCTIONS:
{
"publicStaticFunction": function(pClassA)
{
console.log("[START]classA::publicStaticFunction()");
this.STATIC.privateStaticFunction(5);
console.log(crx_static("classA").publicStaticVar); //OR this.STATIC.publicStaticVar
console.log(this.STATIC.privateStaticVar);
console.log(this.O(pClassA).privateVar);
console.log("[END]classA::publicStaticFunction()");
}
}
},
FUNCTIONS:
{
public: void publicFunction(int pA)
{
cout << "classA::publicFunction()\n";
}
"publicFunction": function(pA)
{
console.log("classA::publicFunction()");
},
public: classA* test(int pA)
{
cout << "[START]classA::test()\n";
this->publicVirtualFunction(5);
this->privateVirtualFunction(5);
this->publicPureVirtualFunction(5);
this->privatePureVirtualFunction(5);
this->protectedPureVirtualFunction(5);
this->publicFunction(5);
this->privateFunction(5);
classA::privateStaticFunction(5);
cout << classA::publicStaticVar;
cout << classA::privateStaticVar;
cout << "[END]classA::test()\n";
return this;
}
"test": function(pA)
{
console.log("[START]classA::test()");
this.publicVirtualFunction(5);
this.privateVirtualFunction(5);
this.publicPureVirtualFunction(5);
this.privatePureVirtualFunction(5);
this.protectedPureVirtualFunction(5);
this.publicFunction(5);
this.privateFunction(5);
this.STATIC.privateStaticFunction(5);
console.log(crx_static("classA").publicStaticVar);
console.log(this.STATIC.privateStaticVar);
console.log("[END]classA::test()");
return this.THIS;
}
},
VIRTUAL:
{
FUNCTIONS:
{
public: virtual void publicVirtualFunction(int pA)
{
cout << "classA::publicVirtualFunction()\n";
}
"publicVirtualFunction": function(pA)
{
console.log("classA::publicVirtualFunction()");
},
public: virtual void publicPureVirtualFunction(int pA) = 0;
"publicPureVirtualFunction": 0
}
}
},
PRIVATE:
{
private: char privateVar[20] = "classA::privateVar\n";
VARS:
{
"privateVar": "classA::privateVar"
},
STATIC:
{
private: static char privateStaticVar[26];
VARS:
{
"privateStaticVar": "classA::privateStaticVar"
},
private: static void privateStaticFunction(int pA)
{
cout << "classA::privateStaticFunction()\n";
}
FUNCTIONS:
{
"privateStaticFunction": function(pA)
{
console.log("classA::privateStaticFunction()");
}
}
},
private: void privateFunction(int pA)
{
cout << "classA::privateFunction()\n";
}
FUNCTIONS:
{
"privateFunction": function(pA)
{
console.log("classA::privateFunction()");
}
},
VIRTUAL:
{
FUNCTIONS:
{
private: virtual void privateVirtualFunction(int pA)
{
cout << "classA::privateVirtualFunction()\n";
}
"privateVirtualFunction": function(pA)
{
console.log("classA::privateVirtualFunction()");
},
private: virtual void privatePureVirtualFunction(int pA) = 0;
"privatePureVirtualFunction": 0
}
}
},
PROTECTED:
{
protected: void protectedFunction(int pA)
{
cout << "classA::protectedFunction()\n";
}
FUNCTIONS:
{
"protectedFunction": function(pA)
{
console.log("classA::protectedFunction()");
}
},
VIRTUAL:
{
FUNCTIONS:
{
protected: virtual void protectedVirtualFunction(int pA)
{
cout << "classA::protectedVirtualFunction()\n";
}
"protectedVirtualFunction": function(pA)
{
console.log("classA::protectedVirtualFunction()");
},
protected: virtual void protectedPureVirtualFunction(int pA) = 0;
"protectedPureVirtualFunction": 0
};
}
}
}
});
char classA::publicStaticVar[25] = "classA::publicStaticVar\n";
char classA::privateStaticVar[26] = "classA::privateStaticVar\n";
class classB: public classA, public InterfaceC, public InterfaceD
{
crx_registerClass("classB",
{
IMPLEMENTS: ["InterfaceC", "InterfaceD"],
EXTENDS: "classA",
PUBLIC:
{
public: classB(int pA)
{cout << "CONSTRUCTING B\n";}
CONSTRUCT: function(pA)
{console.log("CONSTRUCTING B");},
public: char publicVar[19] = "classB::publicVar\n";
VARS:
{
"publicVar": "classB::publicVar"
},
FUNCTIONS:
{
public: void publicFunction(int pA)
{
cout << "classB::publicFunction()\n";
if(pA != 1)
{this->publicFunction(1);}
this->classA::publicFunction(5);
}
"publicFunction": function(pA)
{
console.log("classB::publicFunction()");
if(pA != 1)
{this.publicFunction(1);}
this.PARENT.publicFunction(5);
},
public: classB* test(int pA)
{
cout << "[START]classB::test()\n";
this->publicVirtualFunction(5);
this->publicFunction(5);
((classA*)this)->publicFunction(5);
cout << "[END]classB::test()\n";
return this;
}
"test": function(pA)
{
console.log("[START]classB::test()");
this.publicVirtualFunction(5);
this.publicFunction(5);
this.CAST("classA").publicFunction(5);
console.log("[END]classB::test()");
return this.THIS;
}
},
public: virtual void publicVirtualFunction(int pA)
{
cout << "classB::publicVirtualFunction()\n";
this->classA::publicVirtualFunction(pA);
}
public: virtual void interfaceAFunction(int pA)
{}
public: virtual void interfaceBFunction(int pA)
{}
public: virtual void interfaceCFunction1(int pA)
{}
public: virtual void interfaceCFunction2(int pA)
{}
public: virtual void interfaceDFunction(int pA)
{}
VIRTUAL:
{
FUNCTIONS:
{
"publicVirtualFunction": function(pA)
{
console.log("classB::publicVirtualFunction()");
this.SR(null, "publicVirtualFunction", pA);
},
"interfaceAFunction": function(pA)
{},
"interfaceBFunction": function(pA)
{},
"interfaceCFunction1": function(pA)
{},
"interfaceCFunction2": function(pA)
{},
"interfaceDFunction": function(pA)
{}
}
}
};
}
});
class classC: public classB
{
crx_registerClass("classC",
{
EXTENDS: "classB",
PUBLIC:
{
public: classC():classB(5)
{
cout << "CONSTRUCTING C\n";
}
CONSTRUCT: function()
{
this.PARENT.CONSTRUCT(5);
console.log("CONSTRUCTING C");
},
VIRTUAL:
{
public: virtual void publicVirtualFunction(int pA) final
{
cout << "classC::publicVirtualFunction()\n";
this->classA::publicVirtualFunction(pA);
this->classA::publicFunction(pA);
this->classA::publicVar2 = 6;
this->classA::publicVar3[0] = 1;
cout << this->classA::publicVar3[0] << "\n";
}
FINAL:
{
FUNCTIONS:
{
"publicVirtualFunction": function(pA)
{
console.log("classC::publicVirtualFunction()");
this.SR("classA", "publicVirtualFunction", pA);
this.SR("classA", "publicFunction", pA);
this.SR("classA", "publicVar2", 6);
this.SR("classA", "publicVar3")[0] = 1;
console.log(this.SR("classA", "publicVar3")[0]);
}
}
},
public: virtual void publicPureVirtualFunction(int pA)
{
cout << "classC::publicPureVirtualFunction()\n";
}
FUNCTIONS:
{
"publicPureVirtualFunction": function(pA)
{
console.log("classC::publicPureVirtualFunction()\n");
}
}
}
},
private: virtual void privatePureVirtualFunction(int pA)
{
cout << "classC::privatePureVirtualFunction()\n";
}
PRIVATE:
{
VIRTUAL:
{
FUNCTIONS:
{
"privatePureVirtualFunction": function(pA)
{
console.log("classC::privatePureVirtualFunction()");
}
}
}
},
protected: virtual void protectedPureVirtualFunction(int pA)
{
cout << "classC::protectedPureVirtualFunction()\n";
}
PROTECTED:
{
VIRTUAL:
{
FUNCTIONS:
{
"protectedPureVirtualFunction": function(pA)
{
console.log("classC::protectedPureVirtualFunction()");
}
}
}
}
};
});
class classD
{
public: void test(classA* pClassA)
{
cout << "[START]classD::test()\n";
cout << pClassA->privateVar;
pClassA->privatePureVirtualFunction(5);
pClassA->protectedFunction(5);
cout << "[END]classD::test()\n";
}
};
crx_registerClass('classD',
{
PUBLIC:
{
FUNCTIONS:
{
"test": function(pClassA)
{
console.log("[START]classD::test()");
console.log(this.O(pClassA, "classA").privateVar);
this.O(pClassA, "classA").privatePureVirtualFunction(5);
this.O(pClassA, "classA").protectedFunction(5);
console.log("[END]classD::test()");
}
}
}
});
int main()
{
classC* a = new classC();
classD* b = new classD();
var a = crx_new("classC");
var b = crx_new("classD");
a->test(5);
((classA*)a)->test(5);
classA::publicStaticFunction((classA*)a);
b->test((classA*)a);
a.test(5);
a.CAST("classA").test(5);
crx_static("classA").publicStaticFunction(a.CAST("classA"));
b.test(a.CAST("classA"));
return 0;
}
CONSTRUCTING B
CONSTRUCTING C
[START]classB::test()
classC::publicVirtualFunction()
classA::publicVirtualFunction()
classA::publicFunction()
1
classB::publicFunction()
classB::publicFunction()
classA::publicFunction()
classA::publicFunction()
classA::publicFunction()
[END]classB::test()
[START]classA::test()
classC::publicVirtualFunction()
classA::publicVirtualFunction()
classA::publicFunction()
1
classA::privateVirtualFunction()
classC::publicPureVirtualFunction()
classC::privatePureVirtualFunction()
classC::protectedPureVirtualFunction()
classA::publicFunction()
classA::privateFunction()
classA::privateStaticFunction()
classA::publicStaticVar
classA::privateStaticVar
[END]classA::test()
[START]classA::publicStaticFunction()
classA::privateStaticFunction()
classA::publicStaticVar
classA::privateStaticVar
classA::privateVar
[END]classA::publicStaticFunction()
[START]classD::test()
classA::privateVar
classC::privatePureVirtualFunction()
classA::protectedFunction()
[END]classD::test()
CONSTRUCTING A
CONSTRUCTING C
[START]classB::test()
classC::publicVirtualFunction()
classA::publicVirtualFunction()
classA::publicFunction()
1
classB::publicFunction()
classB::publicFunction()
classA::publicFunction()
classA::publicFunction()
classA::publicFunction()
[END]classB::test()
[START]classA::test()
classC::publicVirtualFunction()
classA::publicVirtualFunction()
classA::publicFunction()
1
classA::privateVirtualFunction()
classC::publicPureVirtualFunction()
classC::privatePureVirtualFunction()
classC::protectedPureVirtualFunction()
classA::publicFunction()
classA::privateFunction()
classA::privateStaticFunction()
classA::publicStaticVar
classA::privateStaticVar
[END]classA::test()
[START]classA::publicStaticFunction()
classA::privateStaticFunction()
classA::publicStaticVar
classA::privateStaticVar
classA::privateVar
[END]classA::publicStaticFunction()
[START]classD::test()
classA::privateVar
classC::privatePureVirtualFunction()
classA::protectedFunction()
[END]classD::test()
#include <iostream>
using namespace std;
class InterfaceA
{
public: virtual void interfaceAFunction(int pA) = 0;
};
crx_registerInterface("InterfaceA",
{
"interfaceAFunction": 0
});
class InterfaceB
{
public: virtual void interfaceBFunction(int pA) = 0;
};
crx_registerInterface("InterfaceB",
{
"interfaceBFunction": 0
});
class InterfaceC : public InterfaceA, public InterfaceB
{
public: virtual void interfaceCFunction1(int pA) = 0;
public: virtual void interfaceCFunction2(int pA) = 0;
};
crx_registerInterface("InterfaceC",
{
INHERITS: ["InterfaceA", "InterfaceB"],
"interfaceCFunction1": 0,
"interfaceCFunction2": 0
});
class InterfaceD
{
public: virtual void interfaceDFunction(int pA) = 0;
};
crx_registerInterface("InterfaceD",
{
"interfaceDFunction": 0
});
class classA
{
friend class classD;
crx_registerClass("classA",
{
"VERBOSE": 1,
"friends": ["classD"],
public: classA()
{cout << "CONSTRUCTING A\n";}
"public CONSTRUCT": function()
{
console.log("CONSTRUCTING A");
},
public: char publicVar[19] = "classA::publicVar\n";
public: int publicVar2 = 5;
public: int publicVar3[5] = {0, 0, 0, 0, 0};
"public var publicVar": "classA::publicVar",
"public var publicVar2": 5,
"public var publicVar3": [0, 0, 0, 0, 0],
public: static char publicStaticVar[25];
"public static var publicStaticVar": "classA::publicStaticVar",
public: static void publicStaticFunction(classA* pClassA)
{
cout << "[START]classA::publicStaticFunction()\n";
classA::privateStaticFunction(5);
cout << classA::publicStaticVar;
cout << classA::privateStaticVar;
cout << pClassA->privateVar;
cout << "[END]classA::publicStaticFunction()\n";
}
"public static function publicStaticFunction": function(pClassA)
{
console.log("[START]classA::publicStaticFunction()");
this.STATIC.privateStaticFunction(5);
console.log(crx_static("classA").publicStaticVar); //OR this.STATIC.publicStaticVar
console.log(this.STATIC.privateStaticVar);
console.log(this.O(pClassA).privateVar);
console.log("[END]classA::publicStaticFunction()");
},
public: void publicFunction(int pA)
{
cout << "classA::publicFunction()\n";
}
"public function publicFunction": function(pA)
{
console.log("classA::publicFunction()");
},
public: classA* test(int pA)
{
cout << "[START]classA::test()\n";
this->publicVirtualFunction(5);
this->privateVirtualFunction(5);
this->publicPureVirtualFunction(5);
this->privatePureVirtualFunction(5);
this->protectedPureVirtualFunction(5);
this->publicFunction(5);
this->privateFunction(5);
classA::privateStaticFunction(5);
cout << classA::publicStaticVar;
cout << classA::privateStaticVar;
cout << "[END]classA::test()\n";
return this;
}
"public function test": function(pA)
{
console.log("[START]classA::test()");
this.publicVirtualFunction(5);
this.privateVirtualFunction(5);
this.publicPureVirtualFunction(5);
this.privatePureVirtualFunction(5);
this.protectedPureVirtualFunction(5);
this.publicFunction(5);
this.privateFunction(5);
this.STATIC.privateStaticFunction(5);
console.log(this.STATIC.publicStaticVar);
console.log(this.STATIC.privateStaticVar);
console.log("[END]classA::test()");
return this.THIS;
},
public: virtual void publicVirtualFunction(int pA)
{
cout << "classA::publicVirtualFunction()\n";
}
"public virtual function publicVirtualFunction": function(pA)
{
console.log("classA::publicVirtualFunction()");
},
public: virtual void publicPureVirtualFunction(int pA) = 0;
"public virtual function publicPureVirtualFunction": 0,
private: char privateVar[20] = "classA::privateVar\n";
"private var privateVar": "classA::privateVar",
private: static char privateStaticVar[26];
"private static var privateStaticVar": "classA::privateStaticVar",
private: static void privateStaticFunction(int pA)
{
cout << "classA::privateStaticFunction()\n";
}
"private static function privateStaticFunction": function(pA)
{
console.log("classA::privateStaticFunction()");
},
private: void privateFunction(int pA)
{
cout << "classA::privateFunction()\n";
}
"private function privateFunction": function(pA)
{
console.log("classA::privateFunction()");
},
private: virtual void privateVirtualFunction(int pA)
{
cout << "classA::privateVirtualFunction()\n";
}
"private virtual function privateVirtualFunction": function(pA)
{
console.log("classA::privateVirtualFunction()");
},
private: virtual void privatePureVirtualFunction(int pA) = 0;
"private virtual function privatePureVirtualFunction": 0,
protected: void protectedFunction(int pA)
{
cout << "classA::protectedFunction()\n";
}
"protected function protectedFunction": function(pA)
{
console.log("classA::protectedFunction()");
},
protected: virtual void protectedVirtualFunction(int pA)
{
cout << "classA::protectedVirtualFunction()\n";
}
"protected virtual function protectedVirtualFunction": function(pA)
{
console.log("classA::protectedVirtualFunction()");
},
protected: virtual void protectedPureVirtualFunction(int pA) = 0;
"protected virtual function protectedPureVirtualFunction": 0
};
});
char classA::publicStaticVar[25] = "classA::publicStaticVar\n";
char classA::privateStaticVar[26] = "classA::privateStaticVar\n";
class classB: public classA, public InterfaceC, public InterfaceD
{
crx_registerClass("classB",
{
"VERBOSE": 1,
"implements": ["InterfaceC", "InterfaceD"],
"extends": "classA",
public: classB(int pA)
{cout << "CONSTRUCTING B\n";}
"public CONSTRUCT": function(pA)
{
console.log("CONSTRUCTING B");
},
public: char publicVar[19] = "classB::publicVar\n";
"public var publicVar": "classB::publicVar",
public: void publicFunction(int pA)
{
cout << "classB::publicFunction()\n";
if(pA != 1)
{this->publicFunction(1);}
this->classA::publicFunction(5);
}
"public function publicFunction": function(pA)
{
console.log("classB::publicFunction()");
if(pA != 1)
{this.publicFunction(1);}
this.PARENT.publicFunction(5);
},
public: classB* test(int pA)
{
cout << "[START]classB::test()\n";
this->publicVirtualFunction(5);
this->publicFunction(5);
((classA*)this)->publicFunction(5);
cout << "[END]classB::test()\n";
return this;
}
"public function test": function(pA)
{
console.log("[START]classB::test()");
this.publicVirtualFunction(5);
this.publicFunction(5);
this.CAST("classA").publicFunction(5);
console.log("[END]classB::test()");
return this.THIS;
},
public: virtual void publicVirtualFunction(int pA)
{
cout << "classB::publicVirtualFunction()\n";
this->classA::publicVirtualFunction(pA);
}
public: virtual void interfaceAFunction(int pA)
{}
public: virtual void interfaceBFunction(int pA)
{}
public: virtual void interfaceCFunction1(int pA)
{}
public: virtual void interfaceCFunction2(int pA)
{}
public: virtual void interfaceDFunction(int pA)
{}
"public virtual function publicVirtualFunction": function(pA)
{
console.log("classB::publicVirtualFunction()");
this.SR(null, "publicVirtualFunction", pA);
},
"public virtual function interfaceAFunction": function(pA)
{},
"public virtual function interfaceBFunction": function(pA)
{},
"public virtual function interfaceCFunction1": function(pA)
{},
"public virtual function interfaceCFunction2": function(pA)
{},
"public virtual function interfaceDFunction": function(pA)
{}
};
});
class classC: public classB
{
crx_registerClass("classC",
{
"VERBOSE": 1,
"extends": "classB",
public: classC():classB(5)
{
cout << "CONSTRUCTING C\n";
}
"public CONSTRUCT": function()
{
this.PARENT.CONSTRUCT(5);
console.log("CONSTRUCTING C");
},
public: virtual void publicVirtualFunction(int pA) final
{
cout << "classC::publicVirtualFunction()\n";
this->classA::publicVirtualFunction(pA);
this->classA::publicFunction(pA);
this->classA::publicVar2 = 6;
this->classA::publicVar3[0] = 1;
cout << this->classA::publicVar3[0] << "\n";
}
"public virtual function publicVirtualFunction": function(pA)
{
console.log("classC::publicVirtualFunction()");
this.SR("classA", "publicVirtualFunction", pA);
this.SR("classA", "publicFunction", pA);
this.SR("classA", "publicVar2", 6);
this.SR("classA", "publicVar3")[0] = 1;
console.log(this.SR("classA", "publicVar3")[0]);
},
public: virtual void publicPureVirtualFunction(int pA)
{
cout << "classC::publicPureVirtualFunction()\n";
}
"public virtual function publicPureVirtualFunction": function(pA)
{
console.log("classC::publicPureVirtualFunction()\n");
},
private: virtual void privatePureVirtualFunction(int pA)
{
cout << "classC::privatePureVirtualFunction()\n";
}
"private virtual function privatePureVirtualFunction": function(pA)
{
console.log("classC::privatePureVirtualFunction()");
},
protected: virtual void protectedPureVirtualFunction(int pA)
{
cout << "classC::protectedPureVirtualFunction()\n";
}
"protected virtual function protectedPureVirtualFunction": function(pA)
{
console.log("classC::protectedPureVirtualFunction()");
}
};
});
class classD
{
public: void test(classA* pClassA)
{
cout << "[START]classD::test()\n";
cout << pClassA->privateVar;
pClassA->privatePureVirtualFunction(5);
pClassA->protectedFunction(5);
cout << "[END]classD::test()\n";
}
};
crx_registerClass('classD',
{
"VERBOSE": 1,
"public function test": function(pClassA)
{
console.log("[START]classD::test()");
console.log(this.O(pClassA, "classA").privateVar);
this.O(pClassA, "classA").privatePureVirtualFunction(5);
this.O(pClassA, "classA").protectedFunction(5);
console.log("[END]classD::test()");
}
});
int main()
{
classC* a = new classC();
classD* b = new classD();
var a = crx_new("classC");
var b = crx_new("classD");
a->test(5);
((classA*)a)->test(5);
classA::publicStaticFunction((classA*)a);
b->test((classA*)a);
a.test(5);
a.CAST("classA").test(5);
crx_static("classA").publicStaticFunction(a.CAST("classA"));
b.test(a.CAST("classA"));
return 0;
}
CONSTRUCTING B
CONSTRUCTING C
[START]classB::test()
classC::publicVirtualFunction()
classA::publicVirtualFunction()
classA::publicFunction()
1
classB::publicFunction()
classB::publicFunction()
classA::publicFunction()
classA::publicFunction()
classA::publicFunction()
[END]classB::test()
[START]classA::test()
classC::publicVirtualFunction()
classA::publicVirtualFunction()
classA::publicFunction()
1
classA::privateVirtualFunction()
classC::publicPureVirtualFunction()
classC::privatePureVirtualFunction()
classC::protectedPureVirtualFunction()
classA::publicFunction()
classA::privateFunction()
classA::privateStaticFunction()
classA::publicStaticVar
classA::privateStaticVar
[END]classA::test()
[START]classA::publicStaticFunction()
classA::privateStaticFunction()
classA::publicStaticVar
classA::privateStaticVar
classA::privateVar
[END]classA::publicStaticFunction()
[START]classD::test()
classA::privateVar
classC::privatePureVirtualFunction()
classA::protectedFunction()
[END]classD::test()
CONSTRUCTING A
CONSTRUCTING C
[START]classB::test()
classC::publicVirtualFunction()
classA::publicVirtualFunction()
classA::publicFunction()
1
classB::publicFunction()
classB::publicFunction()
classA::publicFunction()
classA::publicFunction()
classA::publicFunction()
[END]classB::test()
[START]classA::test()
classC::publicVirtualFunction()
classA::publicVirtualFunction()
classA::publicFunction()
1
classA::privateVirtualFunction()
classC::publicPureVirtualFunction()
classC::privatePureVirtualFunction()
classC::protectedPureVirtualFunction()
classA::publicFunction()
classA::privateFunction()
classA::privateStaticFunction()
classA::publicStaticVar
classA::privateStaticVar
[END]classA::test()
[START]classA::publicStaticFunction()
classA::privateStaticFunction()
classA::publicStaticVar
classA::privateStaticVar
classA::privateVar
[END]classA::publicStaticFunction()
[START]classD::test()
classA::privateVar
classC::privatePureVirtualFunction()
classA::protectedFunction()
[END]classD::test()

1.3.1 Tree Syntax
Look at Figure 1, the Tree Syntax tab. There are Few things to note:
- Interfaces and classes are defined using Objects. These objects are called Definitions. This also applies to the verbose syntax.
- In the tree syntax, definitions are essentially a tree, and when using the tree syntax, all nodes in a class definition apart from the leaves are definition keywords.
- The only definition keyword in interface definitions is "EXTEND". This also applies to the verbose syntax.
- Definition keywords are case sensitive.
- Definition keywords must be nested in exactly the same places as in the figure above. For example, "VARS" can only appear directly under any of "STATIC", "PUBLIC" and "PRIVATE". The ordering is not important.
- The output is the same as the C++ output except for the order of the constructors' output. Please refer to that section to understand why and how the order can be made to match that of C++. This also applies to the verbose syntax.

1.3.2 Verbose Syntax
Look at Figure 1, the Verbose Syntax tab. There are Few things to note:
- The syntax is the same as the Tree Syntax except for the class definitions. The interface definitions syntax is the same as before.
- Definition keywords in the class definitions are not case sensitive, apart from the definition keyword "CONSTRUCT" and "VERBOSE". Throughout this documentation, we use lower case on the definition keywords to emphasize this fact, however in your own code it is recommended that you use upper case on them for easier readability.
- The definition keyword "VERBOSE" is mandatory, and it must appear as the first line in the definition set as 1. Currently, CrxOop does not check for the value 1, but it might in the future, so use 1. "VERBOSE" is what defines the definition to be using the verbose syntax.
- It might be good to understand that internally, the class verbose definition is converted to a class tree definition before further processing.
- The code is equivalent to the code using the tree syntax and hence the output is the same.
- As with the tree syntax code, The output is the same as the C++ output except for the order of the constructors' output. Please refer to that section to understand why and how the order can be made to match that of C++.

1.3.3 Keywords
Refer back to Figure 1. CrxOop provides a number of 'keywords' to provide its functionality, most of which appear in the JS code in the figure, and can be understood by comparing the JS code to that of C++. The 'keywords' fall under three types:
-
Definition Keywords: These keywords are available in class
definitions and interface
definitions. In the
tree syntax, they are
object property names, and are case sensitive. In the
verbose syntax they are part of object property names
and are case insensitive apart for "VERBOSE" and "CONSTRUCT". Also note that
in the verbose syntax, VARS becomes VAR, and FUNCTIONS becomes
FUNCTION. Further more, but not shown in Figure 1, "CONSTS" becomes "CONST".
The following is the list of these keywords: VERBOSE, FRIENDS, IMPLEMENTS, EXTENDS, PUBLIC, PROTECTED, PRIVATE, CONSTRUCT, VARS, FUNCTIONS, STATIC, and CONSTS.
-
Class Keywords: These keywords are used in the class instance functions, whether public, protected, private,
public virtual, protected virtual, or private virtual.
They are property names of actual variables and functions placed on the object pointed to by the javascript
keyword "this" in the instance function. Hence they are case sensitive. These keywords, along with the native
"this", which is found in both instance and class functions, and apart from
"THIS", and
"PARENT", must never
be passed around as function parameters and function
returns when accessed using "this". This rule also applies to the return of
"O"(), i.e. the value returned by
this.O().
The following is the list of these keywords: O, THIS, PARENT, CONSTRUCT, SR, STATIC, CAST, and ANNUL.
Needless to say, class keywords are not found in static functions, except for this, "STATIC" and "O".
-
Global Keywords: In the browser, these keywords are global javascript functions that can be used any where, and hence they are
case sensitive. In Node.js, this may or may not be the case. Please refer to the section on
Node.js usage.
The following is the list of these keywords: crx_registerClass(), crx_registerInterface, crx_new and crx_static.
Keep in mind that all keywords, and typing functions which will be discussed later and are provided by CrxOop, can lead to fatal errors if used incorrectly. In other words, treat them all as actual keywords, the same way you do with native keywords in C++ and other languages. In other languages a misused keyword will either mean compilation failure, or runtime halt if the language is interpreted.

1.3.4 Summary Of Supported Features
The following is a list of the high level features supported in CrxOop, which are usually seen in C++ and other OOP languages:
- Classes
- Class public constructors
- Class public and private variables. Public variables are supported on roughly IE9 era browsers and later.
- Class public, protected and private functions
- Class public virtual, protected virtual, and private virtual functions, including support for "finals" and pure virtual functions. Pure virtual functions can not be finals.
- Casting
- Data Typing. See also Class Typing.
- Class public and private static variables. Private static variables are supported on roughly IE9 era browsers and later.
- Class public and private constants. Private constants are supported on roughly IE9 era browsers and later.
- Class public and private static functions. Private static functions are supported on roughly IE9 era browsers and later.
- Friend Classes
- Abstract Classes
- Anonymous Classes
- Interfaces (a Class with only public pure virtual methods)

1.4 POBP (Structures) Overview
Where the OOP part of CrxOop is an implementation of a pre existing paradigm, Structures are this author's vision, formalation, and generalization of prototypal inheritance and how it would work if javascript was a non scripting for rapid development, non strict, language like C++ or Java. In other words, structure are CrxOop's implementation of Prototype Based Programming, or as this author likes to call it, Prototype Object Based Programming (POBP).
Traditionaly, using plain javascript, different programmers resulted to different methods to implementing various features, such as private access, when coding prototype based data types. Each method had its own shortcoming, and non were complete. CrxOop introduces well defined fully functional implementation of POBP as this author envisions it. It should be noted that CrxOop's implementation is a super set of what traditionaly programmers envision when they apply their favorite method to code prototype based data types and the various effects that they want. CrxOop calls the prototype based data types, Structures.

1.4.1 Summary Of Supported Features
The following is a list of the high level features implemented in CrxOop for POPB, and an explanation for each.
- Shared Public Scope (Function): When a function member has this scope, it is equivilant to setting the function on the prototype of the constructor function when coding using plain javscript. One, hence the word 'shared'. Two, there exists only one data entry for each function global to all objects of a particular prototype based data type. This would be contrary to setting the function on 'this' in the constructor function when coding using plain javascript.
- Private Scope (Variables and Functions): When a variable member, or a function member, has this scope the member can only be accessed on functions of the same structure. This is usualy enforced in plain javascript by declaring each such members as 'var' in the constructor function taking advantage of closures. However, unlike the plain javascript case, this privacy is across structure instances. Refer to the structure keyword 'O'.
- Public Constructors: The public scope is supported for constructors only. The scope, and constructors act as expected when coding using plain javascript, but with the added benefit of the guaranteed default contstructor invocation like in the OOP case.
- Multiple Inheritance.
- Data Typing. See also Structrure Typing.
- Iteration using the native javscript built "for in" loop mechanism. CrxOop provides HASOWN(), an equivilant to the built in hasOwnProperty().

1.4.2 POBP Syntax Overview
CrxOop's POBP facility offers two syntaxes for defining Structures, and one syntax for everything else. The following illustration should cover quickly most of the features supported, apart from iteration.
crx_registerStructure('structureA',
{
PUBLIC:
{
CONSTRUCT: function()
{
console.log('CONSTRUCTING A');
}
},
PRIVATE:
{
VARS:
{
privateVar: "structureA::privateVar"
},
FUNCTIONS:
{
privateFunction: function()
{
return "structureA::privateFunction()";
}
}
},
SHARED:
{
PUBLIC:
{
VARS:
{
sharedPublicVar1: "structureA::sharedPublicVar1"
},
FUNCTIONS:
{
sharedPublicFunction1: function(pA)
{
console.log("I am structureA::sharedPublicFunction1() and I " +
"recieved parameter " + pA + " and I see " +
this.privateVar + " and " + this.privateFunction() + " and " +
this.sharedPublicVar1 + " and " + this.sharedPublicVar2 + " and " +
this.sharedPrivateVar1);
console.log("---------");
}
}
},
PRIVATE:
{
VARS:
{
sharedPrivateVar1: "structureA::sharedPrivateVar1"
},
FUNCTIONS:
{
sharedPrivateFunction1: function(pA)
{
console.log("I am structureA::sharedPrivateFunction1() and I " +
"recieved parameter " + pA + " and I see " +
this.privateVar + " and " + this.privateFunction() + " and " +
this.sharedPublicVar1 + " and " + this.sharedPublicVar2 + " and " +
this.sharedPrivateVar1);
console.log("---------");
},
sharedPrivateFunction2: function(pA)
{
console.log("I am structureA::sharedPrivateFunction2() and I " +
"recieved parameter " + pA + " and I see " +
this.privateVar + " and " + this.privateFunction() + " and " +
this.sharedPublicVar1 + " and " + this.sharedPublicVar2 + " and " +
this.sharedPrivateVar1);
console.log("---------");
}
}
}
}
});
crx_registerStructure('structureB',
{
PUBLIC:
{
CONSTRUCT: function()
{
console.log('CONSTRUCTING B');
}
},
PRIVATE:
{
VARS:
{
privateVar: "structureB::privateVar"
},
FUNCTIONS:
{
privateFunction: function()
{
return "structureB::privateFunction()";
}
}
},
SHARED:
{
PUBLIC:
{
VARS:
{
sharedPublicVar2: "structureB::sharedPublicVar2"
},
FUNCTIONS:
{
test2: function(pStructureB_Or_C_Or_D)
{
console.log("I am structureB::test2() and I can see the private shared " +
"variable, that I defined, in another instance. The value is " +
this.O(pStructureB_Or_C_Or_D).sharedPrivateVar1);
console.log("---------");
}
}
},
PRIVATE:
{
VARS:
{
sharedPrivateVar1: "structureB::sharedPrivateVar1"
},
FUNCTIONS:
{
sharedPrivateFunction2: function(pA)
{
console.log("I am structureB::sharedPrivateFunction2() and I " +
"recieved parameter " + pA + " and I see " +
this.privateVar + " and " + this.privateFunction() + " and " +
this.sharedPublicVar1 + " and " + this.sharedPublicVar2 + " and " +
this.sharedPrivateVar1);
console.log("---------");
}
}
}
}
});
crx_registerStructure('structureC',
{
INHERITS: ['structureB'],
PUBLIC:
{
CONSTRUCT: function(pA)
{
console.log('CONSTRUCTING C: Passed ' + pA);
}
},
PRIVATE:
{
VARS:
{
privateVar: "structureC::privateVar"
},
FUNCTIONS:
{
privateFunction: function()
{
return "structureC::privateFunction()";
}
}
},
SHARED:
{
PUBLIC:
{
VARS:
{
sharedPublicVar1: "structureC::sharedPublicVar1"
}
},
PRIVATE:
{
VARS:
{
sharedPrivateVar1: "structureC::sharedPrivateVar1"
}
}
}
});
crx_registerStructure('structureD',
{
INHERITS: ['structureA', 'structureC'],
PUBLIC:
{
CONSTRUCT: function(pA)
{
this.CONSTRUCT('structureC')(pA);
console.log('CONSTRUCTING D');
}
},
PRIVATE:
{
VARS:
{
privateVar: "structureD::privateVar"
},
FUNCTIONS:
{
privateFunction: function()
{
return "structureD::privateFunction()";
}
}
},
SHARED:
{
PUBLIC:
{
FUNCTIONS:
{
sharedPublicFunction1: function(pA)
{
console.log("I am structureD::sharedPublicFunction1() and I " +
"recieved parameter " + pA + " and I see " +
this.privateVar + " and " + this.privateFunction() + " and " +
this.sharedPublicVar1 + " and " + this.sharedPublicVar2 + " and " +
this.sharedPrivateVar1);
console.log("---------");
},
test: function()
{
this.sharedPrivateFunction1(21);
this.sharedPrivateFunction2(24);
this.SR("structureA", "sharedPrivateFunction1", 28);
}
}
},
PRIVATE:
{
VARS:
{
sharedPrivateVar1: "structureD::sharedPrivateVar1"
},
FUNCTIONS:
{
sharedPrivateFunction1: function(pA)
{
console.log("I am structureD::sharedPrivateFunction1() and I " +
"recieved parameter " + pA + " and I see " +
this.privateVar + " and " + this.privateFunction() + " and " +
this.sharedPublicVar1 + " and " + this.sharedPublicVar2 + " and " +
this.sharedPrivateVar1);
console.log("---------");
}
}
}
}
});
var a = crx_new("structureD", 123);
a.sharedPublicFunction1(10);
a.SR("structureA", "sharedPublicFunction1", 11);
a.test();
a.test2(crx_new("structureC", 99));
CONSTRUCTING B
CONSTRUCTING D
CONSTRUCTING A
I am structureD::sharedPublicFunction1() and I recieved parameter 10 and I see structureD::privateVar and structureD::privateFunction() and structureC::sharedPublicVar1 and structureB::sharedPublicVar2 and structureD::sharedPrivateVar1
---------
I am structureA::sharedPublicFunction1() and I recieved parameter 11 and I see structureA::privateVar and structureA::privateFunction() and structureC::sharedPublicVar1 and structureB::sharedPublicVar2 and structureD::sharedPrivateVar1
---------
I am structureD::sharedPrivateFunction1() and I recieved parameter 21 and I see structureD::privateVar and structureD::privateFunction() and structureC::sharedPublicVar1 and structureB::sharedPublicVar2 and structureD::sharedPrivateVar1
---------
I am structureB::sharedPrivateFunction2() and I recieved parameter 24 and I see structureB::privateVar and structureB::privateFunction() and structureC::sharedPublicVar1 and structureB::sharedPublicVar2 and structureD::sharedPrivateVar1
---------
I am structureA::sharedPrivateFunction1() and I recieved parameter 28 and I see structureA::privateVar and structureA::privateFunction() and structureC::sharedPublicVar1 and structureB::sharedPublicVar2 and structureD::sharedPrivateVar1
---------
CONSTRUCTING C: Passed 99
CONSTRUCTING B
I am structureB::test2() and I can see the private shared variable, that I defined, in another instance. The value is structureC::sharedPrivateVar1
---------
crx_registerStructure('structureA',
{
VERBOSE: 1,
"public CONSTRUCT": function()
{
console.log('CONSTRUCTING A');
},
"private var privateVar": "structureA::privateVar",
"private function privateFunction": function()
{
return "structureA::privateFunction()";
},
"shared public var sharedPublicVar1": "structureA::sharedPublicVar1",
"shared public function sharedPublicFunction1": function(pA)
{
console.log("I am structureA::sharedPublicFunction1() and I " +
"recieved parameter " + pA + " and I see " +
this.privateVar + " and " + this.privateFunction() + " and " +
this.sharedPublicVar1 + " and " + this.sharedPublicVar2 + " and " +
this.sharedPrivateVar1);
console.log("---------");
},
"shared private var sharedPrivateVar1": "structureA::sharedPrivateVar1",
"shared private function sharedPrivateFunction1": function(pA)
{
console.log("I am structureA::sharedPrivateFunction1() and I " +
"recieved parameter " + pA + " and I see " +
this.privateVar + " and " + this.privateFunction() + " and " +
this.sharedPublicVar1 + " and " + this.sharedPublicVar2 + " and " +
this.sharedPrivateVar1);
console.log("---------");
},
"shared private function sharedPrivateFunction2": function(pA)
{
console.log("I am structureA::sharedPrivateFunction2() and I " +
"recieved parameter " + pA + " and I see " +
this.privateVar + " and " + this.privateFunction() + " and " +
this.sharedPublicVar1 + " and " + this.sharedPublicVar2 + " and " +
this.sharedPrivateVar1);
console.log("---------");
}
});
crx_registerStructure('structureB',
{
VERBOSE: 1,
"public CONSTRUCT": function()
{
console.log('CONSTRUCTING B');
},
"private var privateVar": "structureB::privateVar",
"private function privateFunction": function()
{
return "structureB::privateFunction()";
},
"shared public var sharedPublicVar2": "structureB::sharedPublicVar2",
"shared public function test2": function(pStructureB_Or_C_Or_D)
{
console.log("I am structureB::test2() and I can see the private shared " +
"variable, that I defined, in another instance. The value is " +
this.O(pStructureB_Or_C_Or_D).sharedPrivateVar1);
console.log("---------");
},
"shared private var sharedPrivateVar1": "structureB::sharedPrivateVar1",
"shared private function sharedPrivateFunction2": function(pA)
{
console.log("I am structureB::sharedPrivateFunction2() and I " +
"recieved parameter " + pA + " and I see " +
this.privateVar + " and " + this.privateFunction() + " and " +
this.sharedPublicVar1 + " and " + this.sharedPublicVar2 + " and " +
this.sharedPrivateVar1);
console.log("---------");
}
});
crx_registerStructure('structureC',
{
VERBOSE: 1,
INHERITS: ['structureB'],
"public CONSTRUCT": function(pA)
{
console.log('CONSTRUCTING C: Passed ' + pA);
},
"private var privateVar": "structureC::privateVar",
"private function privateFunction": function()
{
return "structureC::privateFunction()";
},
"shared public var sharedPublicVar1": "structureC::sharedPublicVar1",
"shared private var sharedPrivateVar1": "structureC::sharedPrivateVar1"
});
crx_registerStructure('structureD',
{
VERBOSE: 1,
INHERITS: ['structureA', 'structureC'],
"public CONSTRUCT": function(pA)
{
this.CONSTRUCT('structureC')(pA);
console.log('CONSTRUCTING D');
},
"private var privateVar": "structureD::privateVar",
"private function privateFunction": function()
{
return "structureD::privateFunction()";
},
"shared public function sharedPublicFunction1": function(pA)
{
console.log("I am structureD::sharedPublicFunction1() and I " +
"recieved parameter " + pA + " and I see " +
this.privateVar + " and " + this.privateFunction() + " and " +
this.sharedPublicVar1 + " and " + this.sharedPublicVar2 + " and " +
this.sharedPrivateVar1);
console.log("---------");
},
"shared public function test": function()
{
this.sharedPrivateFunction1(21);
this.sharedPrivateFunction2(24);
this.SR("structureA", "sharedPrivateFunction1", 28);
},
"shared private var sharedPrivateVar1": "structureD::sharedPrivateVar1",
"shared private function sharedPrivateFunction1": function(pA)
{
console.log("I am structureD::sharedPrivateFunction1() and I " +
"recieved parameter " + pA + " and I see " +
this.privateVar + " and " + this.privateFunction() + " and " +
this.sharedPublicVar1 + " and " + this.sharedPublicVar2 + " and " +
this.sharedPrivateVar1);
console.log("---------");
}
});
var a = crx_new("structureD", 123);
a.sharedPublicFunction1(10);
a.SR("structureA", "sharedPublicFunction1", 11);
a.test();
a.test2(crx_new("structureC", 99));
CONSTRUCTING B
CONSTRUCTING D
CONSTRUCTING A
I am structureD::sharedPublicFunction1() and I recieved parameter 10 and I see structureD::privateVar and structureD::privateFunction() and structureC::sharedPublicVar1 and structureB::sharedPublicVar2 and structureD::sharedPrivateVar1
---------
I am structureA::sharedPublicFunction1() and I recieved parameter 11 and I see structureA::privateVar and structureA::privateFunction() and structureC::sharedPublicVar1 and structureB::sharedPublicVar2 and structureD::sharedPrivateVar1
---------
I am structureD::sharedPrivateFunction1() and I recieved parameter 21 and I see structureD::privateVar and structureD::privateFunction() and structureC::sharedPublicVar1 and structureB::sharedPublicVar2 and structureD::sharedPrivateVar1
---------
I am structureB::sharedPrivateFunction2() and I recieved parameter 24 and I see structureB::privateVar and structureB::privateFunction() and structureC::sharedPublicVar1 and structureB::sharedPublicVar2 and structureD::sharedPrivateVar1
---------
I am structureA::sharedPrivateFunction1() and I recieved parameter 28 and I see structureA::privateVar and structureA::privateFunction() and structureC::sharedPublicVar1 and structureB::sharedPublicVar2 and structureD::sharedPrivateVar1
---------
CONSTRUCTING C: Passed 99
CONSTRUCTING B
I am structureB::test2() and I can see the private shared variable, that I defined, in another instance. The value is structureC::sharedPrivateVar1
---------

1.4.2.1 Tree Syntax
Look at Figure 2, the Tree Syntax tab. There are Few things to note:
- Structures are defined using Objects. These objects are called Definitions. This also applies to the verbose syntax.
- In the tree syntax, definitions are essentially a tree, and when using the tree syntax, all nodes in a structure definition apart from the leaves are definition keywords.
- Definition keywords are case sensitive.
- Definition keywords must be nested in exactly the same places as in the figure above. For example, "VARS" can only appear directly under any of "PUBLIC" and "PRIVATE". The ordering is not important.
- Please refer to the section about constructors to understand the order of construction.

1.4.2.2 Verbose Syntax
Look at Figure 2, the Verbose Syntax tab. There are Few things to note:
- The syntax is the same as the Tree Syntax except for the structure definitions.
- Definition keywords in the structure definitions are not case sensitive, apart from the definition keyword "CONSTRUCT" and "VERBOSE". Throughout this documentation, we use lower case on the definition keywords to emphasize this fact, however in your own code it is recommended that you use upper case on them for easier readability.
- The definition keyword "VERBOSE" is mandatory, and it must appear as the first line in the definition set to 1. Currently, CrxOop does not check for the value 1, but it might in the future, so use 1. "VERBOSE" is what defines the definition to be using the verbose syntax.
- It might be good to understand that internally, the structure verbose definition is converted to a structure tree definition before further processing.
- The code is equivalent to the code using the tree syntax and hence the output is the same.
- Please refer to the section about constructors to understand the order of construction.

1.4.3 Keywords
Refer back to Figure 2. CrxOop provides a number of 'keywords' to provide its functionality, most of which appear in the JS code in the figure. The 'keywords' fall under three types:
- Definition Keywords: These keywords are available in structure definitions. In the tree syntax, they are object property names, and are case sensitive. In the verbose syntax they are part of object property names and are case insensitive apart for "VERBOSE" and "CONSTRUCT". Also note that in the CONSTRUCT, VARS, and FUNCTIONS.
-
Structure Keywords: These keywords are used in the structure instance functions, whether shared public,
shared private, public, or prviate. They are property names of actual variables and functions
placed on the object pointed to by the javascript keyword "this" in the instance function. Hence
they are case sensitive. These keywords, along with the native "this", and apart from
"THIS" must never
be passed around as function parameters and function
returns when accessed using "this". This rule also applies to the return of
"O"(), i.e. the value returned by
this.O().
The following is the list of these keywords: O, THIS, CONSTRUCT, SR, and HASOWN.
-
Global Keywords: These keywords are global javascript functions that can be used any where, and hence they are
case sensitive.
The following is the list of these keywords: crx_registerStructure, and crx_new.
Keep in mind that all keywords, and typing functions which will be discussed later and are provided by CrxOop, can lead to fatal errors if used incorrectly. In other words, treat them all as actual keywords, the same way you do with native keywords in C++ and other languages. In other languages a misused keyword will either mean compilation failure, or runtime halt if the language is interpreted.

1.5 Modes of Operation
The library has two modes of operation, a flag which I shall call here "mode_crxOop".
Mode_crxOop is related to whether the data structures representing class instances created by CrxOop are structurally protected from tampering with when possible or not. This flag is set explicitly using the function "crxOop.setStrictMode()" which takes a single argument, a boolean for the value of the flag. The function must be called before any class registration or interface registration takes place.
Making the call "crxOop.setStrictMode(false)" is not recommended but we provide it because it can make significant performance difference if you are creating a lot of class instances, which should be a very rare case. Remember that this also compromises security. For that reason, we also provide the function "crxOop.areStructuresLocked()" which takes no arguments and returns true if structure integrity is protected, otherwise false.
Note that not making the call "crxOop.setStrictMode(false)", or explicitly making the call "crxOop.setStrictMode(true)" does not guarantee the call "crxOop.areStructuresLocked()" to return true. Hence, for secure application you might want to consider making the call to "crxOop.areStructuresLocked()" regardless before running your secure code.

2.0 Errors and Warnings
If you are wondering why the documentation is beginning by talking about errors, the answer is because error handling during development with CrxOop is likely to be the most confusing part if not understood properly. There are two types of 'errors', Errors and Warnings. Both are reported in the console of your browser, or IDE, and it is possible to have them reported else where if you so wish.
Warnings are non fatal errors. This means that you can likely continue running your application without problems. However, warnings should not be taken lightly, and are very likely to be turned to errors in the future.
Errors are fatal, and the most common are Definition Errors. When errors are encountered an exception is thrown that escapes the entirety of CrxOop without being caught. The exception is thrown for the convenience exceptions provide in unrolling the stack, but not because CrxOop expects you to catch them. Whether you catch those exceptions in your code or not, CrxOop will halt. This is done by setting an internal flag before throwing the exception.
When halted, most exposed functions of CrxOop, as well as class instance functions will stop working. When it comes to Definition Errors, the behavior is equivalent to those fatal errors caused by wrong syntax in other interpreted languages like PHP. PHP would continue running until it loads a PHP file that has a syntax error and then it halts immediately. In our case this happens mostly with definitions, not files. CrxOop will continue running until it encounters a definition with wrong 'syntax' and halts then. Note that class explicit registration, interface explicit registration, and structure explicit registration do not trigger parsing of the definitions and hence even if there are definition errors, that part of the code will continue running happily. Parsing only happens when the definition is actually required, such as a call to crx_new().
Starting with v1.6, CrxOop includes support of including a stack trace for most common errors. The stack trace filters out CrxOop inter function calls, leaving you with clean stack trace. The stack trace works best if either Stacktrace.js or TraceKit is available. If both are included, CrxOop gives precedance to TraceKit.
When developing, always look at your console. The console is likely to point you to the line of code that threw the error, or the definition that caused the error. Starting with v1.6, as well as the CrxOop message, you might see two stack traces on the console, one labeled clearly to be from CrxOop, and this is the one you want. If you do not see this, chances are you have an error in the definition itself, and in this case the clue in the error will be in CrxOop's message, not the other stack trace.
When it comes to definition errors you will not get an actual line of code pointing to the error, but you are likely to get the name of the class or interface with the malformed definition. For this reason, it is recommended that classes, interfaces, and structures be explicitly registered. If they are not, you will get a cryptic name of the class or interface where the error is.

2.1 crxOop.setLogger()
CrxOop provides the global function crxOop.setLogger which can be used to route the error messages elsewhere. The function itself does not throw exceptions. By default, messages are sent to the console if the browser supports it. The function must be called before any class registration or interface registration. If called later, the call will fail. If unsure, make the call right after the inclusion line of the library in your html code.
crxOop.setLogger(function(pMessage, pErrorType)
{
if(pErrorType === 0)
{console.log("Fatal Error: " + pMessage);}
else if(pErrorType === 1)
{console.log("Warning: " + pMessage);}
return;
});

3.0 Classes

3.1 Instantiation
The equivalent of the C++ "new" keyword is the function crx_new() / crxOop.crx_new(). The function is the only
way to create class instances. The function provides four overrides which allow the creation of single or multiple
instances. All four overrides allow the use of anonymous class
definitions, a feature which can be
abused very easily.
crx_registerClass("MyClass",
{
"VERBOSE": 1,
"public CONSTRUCT": function(pA, pB)
{
console.log("(" + pA + "," + pB + ")");
}
});
var gMyClass = crx_new("MyClass", 1, 1);
crx_registerClass("MyClass",
{
"VERBOSE": 1,
"public CONSTRUCT": function(pA, pB)
{
console.log("(" + pA + "," + pB + ")");
}
});
var gMyClass = crx_new(2, "MyClass", 1, 1);
(1,1)
crx_registerClass("MyClass",
{
"VERBOSE": 1,
"public CONSTRUCT": function(pA, pB)
{
console.log("(" + pA + "," + pB + ")");
}
});
var gMyClass = crx_new(4, [[1,1], [2,2]], "MyClass");
(2,2)
(2,2)
(2,2)
crx_registerClass("MyClass",
{
"VERBOSE": 1,
"public CONSTRUCT": function(pA, pB)
{
console.log("(" + pA + "," + pB + ")");
}
});
var gMyClass = crx_new(4, function(pIndex)
{
return [pIndex, pIndex * pIndex];
}, "MyClass");
(1,1)
(2,4)
(3,9)

3.2 Definition and Registration
Before class instances can be made, they must be defined using either of the two syntaxes mentioned in the introduction. Definitions must not be created or altered using calles to defineProperty(), seal() or other similar methods.
During definition write up, you will encounter syntax errors, as with every other code you write in javascript. After fixing the syntax, you will likely encounter Definition Errors, which are CrxOop's equivalent of javascript syntax errors. These must also be fixed before anything happens. Please refer to the section on errors for more information.
After a definition, a class can either be resgistered explicitly using crx_registerClass() which would allow you to give it a name, or registered implicitly during calls to other parts of the library, such as crx_new.
Explicit registration is the recommended way of registration, and you can either do it by assigning a definition to a variable and then calling crx_registerClass(). In this case instances can be created using the variable name of the definition, or the registered class name.
var classDefinition =
{
//DEFINITION
};
crx_registerClass("myNameSpace.myClass", classDefinition);
var instance1 = crx_new(classDefinition);
//OR
var instance2 = crx_new("myNameSpace.myClass");
Or by passing the definition immediately to crx_registerClass(), which is our prefered approach:
crx_registerClass("myNameSpace.myClass",
{
//DEFINITION
});
var instance2 = crx_new("myNameSpace.myClass");
Note that there is no actual support for name spaces. The full string "myNameSpace.myClass" is the name of the class, and not just "myClass". However the use of ".", or something similar, is useful to avoid name collisions. Also note that class names can collide with interface names and vice versa. Explicit registration is very useful when it comes to definition errors.
The following is an example of implicit registration:
var classDefinition =
{
//DEFINITION
};
var instance1 = crx_new(classDefinition);
Note that classes registered implicitly can not be re registered explicitly later on. Also note that classes that are not registered implicitly or explicitly do not exist as far as CrxOop is concerned until they are registered. This is important to keep in mind when encountering errors about missing definitions.
With implicit registration, one can define anonymous classes
var instance1 = crx_new(
{
//DEFINITION
});
The above can be convenient, however beware. Every such call initiates an internal class registration, which coupled with the immediate need to build an instance, leads to parsing which means resource consumption. If you need to make more than one instance of a class, do not declare it anonymously. This does not simply mean do not put the call in a loop. The call could also be in a function called multiple times for example. However, remember that you could always make multiple instances of your anonymous class using the array form of crx_new, instead of a loop, and because the call to crx_new is only made once this way, you suffer no extra resource loss.

3.2.1 crx_registerClass() / crxOop.crx_registerClass()
crx_registerClass("myNameSpace.myClass",
{
//DEFINITION
});

3.3 Class Components

3.3.1 Constructor
Look at Figure 01. The function CONSTRUCT is the equivalent of a C++ constructor. Please note the following
- The definition keyword CONSTRUCT is case sensitive even in the verbose syntax.
- A constructor may not throw an exception. This will cause a fatal error.
- A constructor may call the constructor of the base class, one level up, but no more. This means, for example, that the constructor of a class may not call the grand parent class's constructor
- A default constructor is created when no constructor is defined. The default constructor is a function that takes no arguments.
- The class constructor is called automatically with no arguments if the derived/child class constructor did not call it explicitly.
- A constructor may not return any thing.
If you look at figure Figure 01 you will notice that the javascript code has identical output to that of the C++ code in , except for the order of construction. To understand why, and to get the same order as in C++, you need to know the steps of construction. Look at the figure below. Given a class 'C', which extends class 'B', which itself extends class 'A', as an example, construction of an instance of C follows the following steps:
- The instance is fully created. This means that in memory, we now have a single instance that fully contains the three images of 'C', 'B' and 'A'.
- The constructor of the most derived class is called first. In our example, this means the constructor of class 'C'. This is the opposite of C++ and intuition in general.
- After the constructor is called, CrxOop checks whether the constructor of the parent class has been called. If not, the parent class constructor is called. In our example, the parent class would be class 'B'.
- The process repeats until all constructors are called.
- The instance is now locked. This protects from new properties being created on the underlying javascript object.
Consider the following code and output:
crx_registerClass("ClassA",
{
"VERBOSE": 1,
"public CONSTRUCT": function(pA)
{
console.log("CONSTRUCTING ClassA using pA = " + pA);
}
});
crx_registerClass("ClassB",
{
"VERBOSE": 1,
"extends": "ClassA",
"public CONSTRUCT": function(pA)
{
console.log("CONSTRUCTING ClassB using pA = " + pA);
}
});
crx_registerClass("ClassC",
{
"VERBOSE": 1,
"extends": "ClassB",
"public CONSTRUCT": function(pA)
{
console.log("CONSTRUCTING ClassC using pA = " + pA);
}
});
crx_new("ClassC", 5);
CONSTRUCTING ClassB using pA = undefined
CONSTRUCTING ClassA using pA = undefined
Let us follow what happened:
- The instance was fully created.
- The constructor of ClassC was called with the passed in parameter, 5.
- After the constructor of ClassC finished executing, CrxOop found that the constructor of ClassB was not called and called it without any parameters.
- After the constructor of ClassB finished executing, CrxOop found that the constructor of ClassA was not called and called it without any parameters.
- The new instance is now locked.
Hence to make the constructor of ClassB execute its useful code before that of the constructor of ClassC, we call it as the first line of code in the constructor of ClassC. Changing the code of ClassC to the following:
crx_registerClass("ClassA",
{
.
.
.
crx_registerClass("ClassC",
{
"VERBOSE": 1,
"extends": "ClassB",
"public CONSTRUCT": function(pA)
{
this.PARENT.CONSTRUCT(pA);
console.log("CONSTRUCTING ClassC using pA = " + pA);
}
});
crx_new("ClassC", 5);
CONSTRUCTING ClassA using pA = undefined
CONSTRUCTING ClassC using pA = 5
Let us follow what happened:
- The instance was fully created.
- The constructor of ClassC was called with the passed in parameter, 5, but it called the constructor of ClassB in its first line of code passing the parameter 5, before its own useful code.
- After the constructor of ClassB finished executing, CrxOop found that the constructor of ClassA was not called and called it without any parameters.
- The constructor of ClassC resumed executing its useful code.
- The new instance is now locked.
We are now almost there towards getting the order of construction that we want but not yet. However, one very important thing to notice is that the ancestors of ClassC fully finished executing their constructors before ClassC finished its own. This is important because it is sufficient in practice. If you are the developer of ClassC, and you want the ancestor's constructor to be called first, all you care about is the ancestors of ClassC to finish doing what they need in their constructors before your class begins executing its constructing code. The order of the construction of your ancestors would matter not. If it did, it would have been the worry of the developer of ClassB, and so forth.
We shall now do the same in ClassB, and call the constructor of Class A as the first line:
crx_registerClass("ClassA",
{
.
.
.
crx_registerClass("ClassB",
{
"VERBOSE": 1,
"extends": "ClassA",
"public CONSTRUCT": function(pA)
{
this.PARENT.CONSTRUCT(pA)
console.log("CONSTRUCTING ClassB using pA = " + pA);
}
});
crx_registerClass("ClassC",
{
"VERBOSE": 1,
"extends": "ClassB",
"public CONSTRUCT": function(pA)
{
this.PARENT.CONSTRUCT(pA);
console.log("CONSTRUCTING ClassC using pA = " + pA);
}
});
crx_new("ClassC", 5);
CONSTRUCTING ClassB using pA = 5
CONSTRUCTING ClassC using pA = 5
We now have the effect we usually want. Let us follow what happened:
- The instance was fully created.
- The constructor of ClassC was called with the passed in parameter, 5, but it called the constructor of CLassB in its first line of code passing the parameter 5, before executing its own useful code.
- The constructor of ClassB was called with the passed in parameter, 5, but it called the constructor of CLassA in its first line of code passing the parameter 5, before executing its own useful code.
- After the constructor of ClassA finished executing, ClassB's constructor began executing its useful code.
- After the constructor of ClassB finished executing, ClassC's constructor began executing its useful code.
- The constructor of ClassC finished executing its useful code.
- The new instance is now locked.
Often you do not want to define a constructor, but are forced to because the constructor of the class that your class is extending takes parameters, and so you define your constructor such as it forwards those paramaters to the constructor of the class being extended. Starting with CrxOop v1.6, you can use the value 1 for the constructor, and this will create a constructor that will do just that.
crx_registerClass("ClassA",
{
"VERBOSE": 1,
"public CONSTRUCT": function(pA)
{
console.log("CONSTRUCTING using pA = " + pA);
}
});
crx_registerClass("ClassB1",
{
"VERBOSE": 1,
"extends": "ClassA"
});
crx_registerClass("ClassB2",
{
"VERBOSE": 1,
"extends": "ClassA",
"public CONSTRUCT": 1
});
console.log('Creating Instance of ClassB1:');
crx_new("ClassB1", 5);
console.log('Creating Instance of ClassB2:');
crx_new("ClassB2", 5);
CONSTRUCTING using pA = undefined
Creating Instance of ClassB2:
CONSTRUCTING using pA = 5

3.3.2 Public and Private Instance Variables
Instance variables are variables available to class instances only. The library supports both public and private accessor types. Class instance variables can be accessed using "this", or the instance object. Needless to say class private instance variables can only be accessed using "this". To understand how to access class private instance variables across class instances refer to the section about 'O'.
crx_registerClass("ExampleClass",
{
PUBLIC:
{
VARS:
{
"publicVar1": 5,
"publicVar2": 6
}
},
PRIVATE:
{
VARS:
{
"privateVar1": 7,
"privateVar2": 8
}
}
});
crx_registerClass("ExampleClass",
{
"VERBOSE": 1,
"public var publicVar1": 5,
"public var publicVar2": 6,
"private var privateVar1": 7,
"private var privateVar2": 8
});
Instantiation of class instance variables can take place in the definition as shown above, however it is important to understand that the statement in the definition with the instantiation is executed only once, and further more when an instance is created the result is simply copied over using the assignment operator. Consider the following example:
crx_registerClass("ExampleClass",
{
PUBLIC:
{
VARS:
{
"publicVar": {}
}
}
});
var instance1 = crx_new("ExampleClass");
var instance2 = crx_new("ExampleClass");
instance1.publicVar['someProperty'] = 10;
console.log(instance2.publicVar.someProperty);
crx_registerClass("ExampleClass",
{
"VERBOSE": 1,
"public var publicVar": {}
});
var instance1 = crx_new("ExampleClass");
var instance2 = crx_new("ExampleClass");
instance1.publicVar['someProperty'] = 10;
console.log(instance2.publicVar.someProperty);
You might have expected the above to print "undefined", yet it printed 10. This is because both
instance1.publicVar and instance2.publicVar are pointing to the same object. The statement
"public var publicVar": {}
in the definition executed only once, and the result, a pointer to the object, was copied over for both instances. If you wanted a new object for each instance publicVar variable, you should instantiate the variable in the constructor, but the variable should still be declared in the definition.
WARNING: Assigning Javascript functions to private instance variables is dangerous and must never be done. Assigning Javascript functions to public instance variables should be safe, but can lead to unexpected results, and should also be avoided. For more information see the section on security and crxOop.var().

3.3.3 Public, Protected, and Private Functions
There are three types of class functions. In this section we discuss the first type, which are non static non virtual functions.
crx_registerClass("ExampleClass",
{
PUBLIC:
{
VARS:
{
"publicVar": 5,
},
FUNCTIONS:
{
"publicFunction1": function()
{
console.log("publicFunction1: " + this.publicVar + ", " + this.privateVar);
},
"publicFunction2": function()
{
this.privateFunction1();
this.privateFunction2(20);
this.protectedFunction1();
this.protectedFunction2(21);
return "publicFunction2";
}
}
},
PROTECTED:
{
FUNCTIONS:
{
"protectedFunction1": function()
{
console.log("protectedFunction1: " + this.publicVar + ", " + this.privateVar);
},
"protectedFunction2": function(pA)
{
console.log("protectedFunction2: " + pA);
}
}
},
PRIVATE:
{
VARS:
{
"privateVar": 7
},
FUNCTIONS:
{
"privateFunction1": function()
{
console.log("privateFunction1: " + this.publicVar + ", " + this.privateVar);
},
"privateFunction2": function(pA)
{
console.log("privateFunction2: " + pA);
}
}
}
});
var instance = crx_new("ExampleClass");
instance.publicFunction1();
console.log(instance.publicFunction2());
privateFunction1: 5, 7
privateFunction2: 20
protectedFunction1: 5, 7
protecredFunction2: 21
publicFunction2
crx_registerClass("ExampleClass",
{
"VERBOSE": 1,
"public var publicVar": 5,
"public function publicFunction1": function()
{
console.log("publicFunction1: " + this.publicVar + ", " + this.privateVar);
},
"public function publicFunction2": function()
{
this.privateFunction1();
this.privateFunction2(20);
this.protectedFunction1();
this.protectedFunction2(21);
return "publicFunction2";
},
"protected function protectedFunction1": function()
{
console.log("protectedFunction1: " + this.publicVar + ", " + this.privateVar);
},
"protected function protectedFunction2": function(pA)
{
console.log("protectedFunction2: " + pA);
},
"private var privateVar": 7,
"private function privateFunction1": function()
{
console.log("privateFunction1: " + this.publicVar + ", " + this.privateVar);
},
"private function privateFunction2": function(pA)
{
console.log("privateFunction2: " + pA);
}
});
var instance = crx_new("ExampleClass");
instance.publicFunction1();
console.log(instance.publicFunction2());
privateFunction1: 5, 7
privateFunction2: 20
protectedFunction1: 5, 7
protecredFunction2: 21
publicFunction2
Inside these functions, "this" points to the instance and can be used to access public, protected and private instance functions and virtual functions, and public and private variables as seen above. Needless to say, this is still javascript, so 'this' could end up pointing elsewhere inside these functions if it is used inside an inner function, such as an anonymous function created during the function run.
Note that "this", and other Class Keywords, are not safe to pass around as function parameters and returns. More on that later.
To understand how to access class private and protected instance functions across class instances refer to the section about 'O'.
Note that being non virtual functions, these functions, like class instance variables, suffer from name hiding.

3.3.4 Public, Protected and Private Virtual Functions
There are three types of class functions. In this section we discuss the second type, which are non static virtual functions.
crx_registerClass("ExampleClass",
{
PUBLIC:
{
VIRTUAL:
{
FUNCTIONS:
{
publicVirtualFunction1: function()
{
console.log("publicVirtualFunction1");
},
publicVirtualFunction2: function()
{
console.log("publicVirtualFunction2");
},
publicPureVirtualFunction1: 0
},
FINAL:
{
FUNCTIONS:
{
publicVirtualFinalFunction1: function()
{
console.log("publicVirtualFinalFunction1");
},
publicVirtualFinalFunction2: function()
{
console.log("publicVirtualFinalFunction2");
}
}
}
}
},
PROTECTED:
{
VIRTUAL:
{
FUNCTIONS:
{
protectedVirtualFunction1: function()
{
console.log("protectedVirtualFunction1");
},
protectedVirtualFunction2: function()
{
console.log("protectedVirtualFunction2");
},
protectedPureVirtualFunction1: 0
},
FINAL:
{
FUNCTIONS:
{
protectedVirtualFinalFunction1: function()
{
console.log("protectedVirtualFinalFunction1");
},
protectedVirtualFunction4: function()
{
console.log("protectedVirtualFunction4");
}
}
}
}
},
PRIVATE:
{
VARS:
{
"privateVar": "ExampleClass::privateVar"
},
VIRTUAL:
{
FUNCTIONS:
{
privateVirtualFunction1: function()
{
console.log("privateVirtualFunction1");
},
privateVirtualFunction2: function()
{
console.log("privateVirtualFunction2");
},
privatePureVirtualFunction1: 0
}
}
}
});
crx_registerClass("ExampleSubClass",
{
EXTENDS: "ExampleClass",
PUBLIC:
{
VIRTUAL:
{
FUNCTIONS:
{
publicPureVirtualFunction1: function()
{
console.log("ExampleSubClass::publicPureVirtualFunction1");
}
}
}
},
PROTECTED:
{
VIRTUAL:
{
FUNCTIONS:
{
protectedPureVirtualFunction1: function()
{
console.log("ExampleSubClass::protectedPureVirtualFunction1");
}
}
}
},
PRIVATE:
{
VIRTUAL:
{
FUNCTIONS:
{
privatePureVirtualFunction1: function()
{
console.log("ExampleSubClass::privatePureVirtualFunction1");
}
}
}
}
});
// The following would cause CrxOop to halt, because ExampleClass contains pure
// virtual functions, and thus can not be instatiated. This makes it an
// abstract class.
//var gExampleClass = crx_new("ExampleClass");
// The following would work because ExampleSubClass implements all the pure virtual
// functions found in ExampleClass. If it did not, this would also make it
// an abstract class.
var gExampleSubClass = crx_new("ExampleSubClass");
crx_registerClass("ExampleClass",
{
"VERBOSE": 1,
"public virtual function publicVirtualFunction1": function()
{
console.log("publicVirtualFunction1");
},
"public virtual function publicVirtualFunction2": function()
{
console.log("publicVirtualFunction2");
},
"public virtual function publicPureVirtualFunction1": 0,
"public virtual final function publicVirtualFinalFunction1": function()
{
console.log("publicVirtualFinalFunction1");
},
"public virtual final function publicVirtualFinalFunction2": function()
{
console.log("publicVirtualFinalFunction2");
},
"protected virtual function protectedVirtualFunction1": function()
{
console.log("protectedVirtualFunction1");
},
"protected virtual function protectedVirtualFunction2": function()
{
console.log("protectedVirtualFunction2");
},
"protected virtual function protectedPureVirtualFunction1": 0,
"protected virtual final function protectedVirtualFinalFunction1": function()
{
console.log("protectedVirtualFinalFunction1");
},
"protected virtual final function protectedVirtualFunction4": function()
{
console.log("protectedVirtualFunction4");
},
"private virtual function privateVirtualFunction1": function()
{
console.log("privateVirtualFunction1");
},
"private virtual function privateVirtualFunction2": function()
{
console.log("privateVirtualFunction2");
},
"private virtual function privatePureVirtualFunction1": 0
});
crx_registerClass("ExampleSubClass",
{
VERBOSE: 1,
EXTENDS: "ExampleClass",
"public virtual function publicPureVirtualFunction1": function()
{
console.log("ExampleSubClass::publicPureVirtualFunction1");
},
"protected virtual function protectedPureVirtualFunction1": function()
{
console.log("ExampleSubClass::protectedPureVirtualFunction1");
},
"private virtual function privatePureVirtualFunction1": function()
{
console.log("ExampleSubClass::privatePureVirtualFunction1");
}
});
// The following would cause CrxOop to halt, because ExampleClass contains pure
// virtual functions, and thus can not be instatiated. This makes it an
// abstract class.
//var gExampleClass = crx_new("ExampleClass");
// The following would work because ExampleSubClass implements all the pure virtual
// functions found in ExampleClass. If it did not, this would also make it
// an abstract class.
var gExampleSubClass = crx_new("ExampleSubClass");
publicVirtualFinalFunction1(), publicVirtualFinalFunction2(), protectedVirtualFinalFunction1(), and protectedVirtualFinalFunction2() above are final functions; they can not be overriden in sub classes.
Virtual final functions can not be private.
publicPureVirtualFunction1(), protectedPureVirtualFunction1() and privatePureVirtualFunction1() are pure virtual functions. Classes containing pure virtual functions are Abstract Classes, and can not be instantiated. Classes extending Abstract Classess without fully implementing their pure virtual functions are also Abstract Classes.
Pure virtual functions can not be final.
If you are not familiar with C++, or the concept of virtual functions, refering to the section on class extension might help.
It is important to note, as an example, that like C++, if a derived class was to declate a virtual function f() as private, where it was declared as public in the base class, one could still gain access to f() by casting the instance to the base class. If you are not familiar with C++, an internet search for C++, virtual functions, and the Liskov substitution principle should help.

3.3.5 Public and Private Static Variables, and Constants
Static variables are shared variables among all instances of a particular class. Both public and private accessors are supported. Unlike C++, which has the scope operator "::" to access static variables, Javascript has no such thing, and CrxOop provides the global keyword 'crx_static', and the class keyword 'STATIC' to access those variables. However, unlike STATIC, crx_static can only be used to access public static variables, not private ones. Inside static funtions, you can also use 'this.STATIC' to access private static variables. Hence, Inside your instance functions, instance virtual functions and static functions, use 'this.STATIC.somePublicOrPrivateVariable', and elsewhere use crx_static.
Constants are static variables with constant binding. If the constant was an object, the constant would be the equivilant of a constant pointer in C++ (void * const), and not a pointer to a constant object (void const *). Accessing constants works the same way as accessing other static variables, and follows the same rules mentioned above.
crx_registerClass("ExampleClass",
{
PUBLIC:
{
CONSTS:
{
"publicConstant1": 5,
"publicConstant2": 6
},
STATIC:
{
VARS:
{
"publicStaticVar1": 1,
"publicStaticVar2": 2
}
},
FUNCTIONS:
{
"test": function()
{
console.log(this.STATIC.publicStaticVar2 + "," + this.STATIC.privateStaticVar2);
// We could have also used crx_static for the public variable 'publicStaticVar2',
// which was not necessary here because it is from the same class.
console.log(crx_static('ExampleClass').publicStaticVar2 + "," + this.STATIC.privateStaticVar2);
console.log(this.STATIC.publicConstant2 + "," + this.STATIC.privateConstant1);
// We could have also used crx_static for the public constant 'publicConstant2',
// which was not necessary here because it is from the same class.
console.log(crx_static('ExampleClass').publicConstant2 + "," + this.STATIC.privateConstant1);
}
}
},
PRIVATE:
{
CONSTS:
{
"privateConstant1": 7,
"privateConstant2": 8
},
STATIC:
{
VARS:
{
"privateStaticVar1": 3,
"privateStaticVar2": 4
}
}
}
});
var instance = crx_new("ExampleClass");
instance.test();
console.log("(" + crx_static("ExampleClass").publicStaticVar2 + ")");
console.log("(" + crx_static("ExampleClass").publicConstant1 + ")");
console.log("(" + instance.STATIC.publicStaticVar2 + ")");
console.log("(" + instance.STATIC.publicConstant1 + ")");
2,4
6,7
6,7
(2)
(5)
(2)
(5)
crx_registerClass("ExampleClass",
{
"VERBOSE": 1,
"public const publicConstant1": 5,
"public const publicConstant2": 6,
"public static var publicStaticVar1": 1,
"public static var publicStaticVar2": 2,
"private const privateConstant1": 7,
"private const privateConstant2": 8,
"private static var privateStaticVar1": 3,
"private static var privateStaticVar2": 4,
"public function test": function()
{
console.log(this.STATIC.publicStaticVar2 + "," + this.STATIC.privateStaticVar2);
// We could have also used crx_static for the public variable 'publicStaticVar2',
// which was not necessary here because it is from the same class.
console.log(crx_static('ExampleClass').publicStaticVar2 + "," + this.STATIC.privateStaticVar2);
console.log(this.STATIC.publicConstant2 + "," + this.STATIC.privateConstant1);
// We could have also used crx_static for the public constant 'publicConstant2',
// which was not necessary here because it is from the same class.
console.log(crx_static('ExampleClass').publicConstant2 + "," + this.STATIC.privateConstant1);
}
});
var instance = crx_new("ExampleClass");
instance.test();
console.log("(" + crx_static("ExampleClass").publicStaticVar2 + ")");
console.log("(" + crx_static("ExampleClass").publicConstant1 + ")");
console.log("(" + instance.STATIC.publicStaticVar2 + ")");
console.log("(" + instance.STATIC.publicConstant1 + ")");
2,4
6,7
6,7
(2)
(5)
(2)
(5)
As can be seen above, 'STATIC' can also be used on instances themselves, and gives no access to privates as expected. This usage is more performant, but less clear, and more error prone. With this usage of STATIC, the class type is the type of the instance. If you feel the need to cast the instance to the correct type before using STATIC, use crx_static instead.
Note that on older browsers where defining constant bindings is not possible, each object is provided with its own copy of the constants, increasing memory usage. Furthermore, any usage of crx_static whether to access constants or other statics can be significantly slower on those browser but only on classes that have constants. This is because CrxOop will assert the constants on these browser each time crx_static is used.
WARNING: Assigning Javascript functions to private static variables is dangerous and must never be done. Assigning Javascript functions to public static variables should be safe, but can lead to unexpected results, and should also be avoided. For more information see the section on security and crxOop.var().

3.3.6 Public and Private Static Functions
There are three types of class functions. In this section we discuss the third type, which is static functions. Like static variables, static functions are shared functions among all instances of a particular class. Both public and private accessors are supported. Unlike C++, which has the scope operator "::" to access static variables and static functions, Javascript has no such thing, and CrxOop provides the global keyword 'crx_static', and the class keyword 'STATIC' to access those functions. However, unlike STATIC, crx_static can only be used to access public static functions, not private ones. Inside static funtions, you can also use 'this.STATIC' to access private static functions. Hence, Inside your instance functions, instance virtual functions and static functions, use 'this.STATIC.somePublicOrPrivateStaticFunction()', and elsewhere use crx_static.
Note that inside static functions, 'this', 'this.O', the return of 'this.O()', and 'this.STATIC', must never be returned from the static functions, or passed to other functions as parameters.
crx_registerClass("ExampleClass",
{
PUBLIC:
{
STATIC:
{
VARS:
{
"publicStaticVar": 1
},
FUNCTIONS:
{
"test": function(pExampleClass)
{
// Notice the usage of this.O to access the private variables of an instance
// from the same class
console.log(this.STATIC.publicStaticVar + "," +
this.STATIC.privateStaticVar + "," +
this.O(pExampleClass).privateVar);
// Could have also used crx_static to access a public static member
// from the same class
console.log(crx_static("ExampleClass").publicStaticVar + "," +
this.STATIC.privateStaticVar + "," +
this.O(pExampleClass).privateVar);
}
}
}
},
PRIVATE:
{
VARS:
{
"privateVar": 5
},
STATIC:
{
VARS:
{
"privateStaticVar": 3
}
}
}
});
var instance = crx_new("ExampleClass");
crx_static("ExampleClass").test(instance);
1,3,5
crx_registerClass("ExampleClass",
{
"VERBOSE": 1,
"public static var publicStaticVar": 1,
"public static function test": function(pExampleClass)
{
// Notice the usage of this.O to access the private variables of an instance
// from the same class
console.log(this.STATIC.publicStaticVar + "," +
this.STATIC.privateStaticVar + "," +
this.O(pExampleClass).privateVar);
// Could have also used crx_static to access a public static member
// from the same class
console.log(crx_static("ExampleClass").publicStaticVar + "," +
this.STATIC.privateStaticVar + "," +
this.O(pExampleClass).privateVar);
},
"private var privateVar": 5,
"private static var privateStaticVar": 3
});
var instance = crx_new("ExampleClass");
crx_static("ExampleClass").test(instance);
1,3,5

3.4 Class Extension (aka Inheritance)
CrxOop supports single class inheritance, which will be called here class extension. Consider the following example:
crx_registerClass("ExampleClass1",
{
PUBLIC:
{
VARS:
{
"publicVar": "I am ExampleClass1"
},
FUNCTIONS:
{
"publicFunction": function(pA)
{
console.log(this.publicVar);
},
"test": function(pA)
{
this.publicFunction();
this.publicVirtualFunction();
this.CAST("ExampleClass1").publicFunction(5);
this.CAST("ExampleClass1").publicVirtualFunction(5);
this.SR("ExampleClass1", "publicVirtualFunction", 5);
}
},
VIRTUAL:
{
FUNCTIONS:
{
"publicVirtualFunction": function(pA)
{
console.log(this.publicVar);
}
}
}
}
});
crx_registerClass("ExampleClass2",
{
EXTENDS: "ExampleClass1",
PUBLIC:
{
VARS:
{
"publicVar": "I am ExampleClass2"
},
FUNCTIONS:
{
"publicFunction": function(pA)
{
console.log(this.publicVar);
}
},
VIRTUAL:
{
FUNCTIONS:
{
"publicVirtualFunction": function(pA)
{
console.log(this.publicVar);
}
}
}
}
});
var instance = crx_new("ExampleClass2");
instance.publicFunction();
instance.publicVirtualFunction();
instance.CAST("ExampleClass1").publicFunction(5);
instance.CAST("ExampleClass1").publicVirtualFunction(5);
instance.SR("ExampleClass1", "publicVirtualFunction", 5);
console.log("------------------------------------------");
instance.test();
I am ExampleClass2
I am ExampleClass1
I am ExampleClass2
I am ExampleClass1
------------------------------------------
I am ExampleClass1
I am ExampleClass2
I am ExampleClass1
I am ExampleClass2
I am ExampleClass1
crx_registerClass("ExampleClass1",
{
VERBOSE: 1,
"public var publicVar": "I am ExampleClass1",
"public function publicFunction": function(pA)
{
console.log(this.publicVar);
},
"public function test": function(pA)
{
this.publicFunction();
this.publicVirtualFunction();
this.CAST("ExampleClass1").publicFunction(5);
this.CAST("ExampleClass1").publicVirtualFunction(5);
this.SR("ExampleClass1", "publicVirtualFunction", 5);
},
"public virtual function publicVirtualFunction": function(pA)
{
console.log(this.publicVar);
}
});
crx_registerClass("ExampleClass2",
{
VERBOSE: 1,
"extends": "ExampleClass1",
"public var publicVar": "I am ExampleClass2",
"public function publicFunction": function(pA)
{
console.log(this.publicVar);
},
"public virtual function publicVirtualFunction": function(pA)
{
console.log(this.publicVar);
}
});
var instance = crx_new("ExampleClass2");
instance.publicFunction();
instance.publicVirtualFunction();
instance.CAST("ExampleClass1").publicFunction(5);
instance.CAST("ExampleClass1").publicVirtualFunction(5);
instance.SR("ExampleClass1", "publicVirtualFunction", 5);
console.log("------------------------------------------");
instance.test();
I am ExampleClass2
I am ExampleClass1
I am ExampleClass2
I am ExampleClass1
------------------------------------------
I am ExampleClass1
I am ExampleClass2
I am ExampleClass1
I am ExampleClass2
I am ExampleClass1
Note the following:
- The output by doing the tests on the instance is the same by doing the test using the function 'test' in the extended class except for the first line. This is because publicFunction is a non virtual function and hence it was publicFunction in side ExampleClass1 that was called when using 'this', where publicFunction inside class ExampleClass2 was called when using the variable "instance".
- Notice how casting affects which "publicFunction" gets called. If the call is made on the variable "instance", which is an instance of ExampleClass2, ExampleClass2::publicFunction gets called and not ExampleClass1::publicFunction due to name hiding. If however it is casted to ExampleClass1 first, then ExampleClass1::publicFunction gets called.
- Notice how casting does not affect which "publicVirtualFunction" gets called. This is because the function is virtual, and the call will always resolve to the most derived definition.
- Some time you might want to call a virtual function, and or other variables found up the class chain, for this, CrxOop provides the class keyword 'SR'. The above example is likely self explanatory, but please see the section on 'SR' for more information.

3.5 Friend Classes
A class can declare its own friend classes giving function members of the friend classes the same access its own function members have.
crx_registerClass("ExampleClass1",
{
PRIVATE:
{
VARS:
{
'privateVar': 'I am ExampleClass1'
}
},
PROTECTED:
{
FUNCTIONS:
{
'protectedFunction': function()
{
console.log('protectedFunction: ' + this.privateVar);
}
},
VIRTUAL:
{
FUNCTIONS:
{
'protectedVirtualFunction': function()
{
console.log('protectedVirtualFunction: ' + this.privateVar);
}
}
}
}
});
crx_registerClass("ExampleClass2",
{
EXTENDS: "ExampleClass1",
FRIENDS: ["FriendOfExampleClass2"],
PRIVATE:
{
VARS:
{
'privateVar': 'I am ExampleClass2'
},
FUNCTIONS:
{
'privateFunction': function()
{
console.log('privateFunction: ' + this.privateVar);
}
},
VIRTUAL:
{
FUNCTIONS:
{
'privateVirtualFunction': function()
{
console.log('privateVirtualFunction: ' + this.privateVar);
}
}
},
STATIC:
{
FUNCTIONS:
{
'privateStaticFunction': function(pExampleClass2)
{
console.log('privateStaticFunction: ' + this.O(pExampleClass2).privateVar);
this.O(pExampleClass2).SR("ExampleClass1", "protectedFunction");
this.O(pExampleClass2).protectedFunction();
this.O(pExampleClass2).protectedVirtualFunction();
}
}
}
},
PROTECTED:
{
FUNCTIONS:
{
'protectedFunction': function()
{
console.log('protectedFunction: ' + this.privateVar);
}
}
}
});
crx_registerClass("FriendOfExampleClass2",
{
PUBLIC:
{
FUNCTIONS:
{
'test1': function(pExampleClass2)
{
// FriendOfExampleClass2 being a friend class of ExampleClass2,
// its instance functions can gain access to the private memory
// area of instances of ExampleClass2 using 0().
console.log(this.O(pExampleClass2, "ExampleClass2").privateVar);
this.O(pExampleClass2, "ExampleClass2").SR("ExampleClass1", "protectedFunction");
this.O(pExampleClass2, "ExampleClass2").protectedFunction();
this.O(pExampleClass2, "ExampleClass2").protectedVirtualFunction();
// Although FriendOfExampleClass2 is a friend class of ExampleClass2,
// its intance functions can not gain access to private static
// members of ExampleClass2 directly. Hence the following
// would not work.
//crx_static("ExampleClass2").privateStaticFunction(pExampleClass2);
// Although many solutions exist for this, non of them lead to a
// satisfactory syntax, and hence why there is currently no
// support for that in CrxOop. A work around is to gain
// access to the private static members of ExampleClass2
// using an instance of ExampleClass2.
this.O(pExampleClass2, "ExampleClass2").STATIC.privateStaticFunction(pExampleClass2);
}
},
STATIC:
{
FUNCTIONS:
{
'test2': function(pExampleClass2)
{
// FriendOfExampleClass2 being a friend class of ExampleClass2,
// its static functions can gain access to the private memory
// area of instances of ExampleClass2 using O().
console.log(this.O(pExampleClass2, "ExampleClass2").privateVar);
this.O(pExampleClass2, "ExampleClass2").SR("ExampleClass1", "protectedFunction");
this.O(pExampleClass2, "ExampleClass2").protectedFunction();
this.O(pExampleClass2, "ExampleClass2").protectedVirtualFunction();
// Although FriendOfExampleClass2 is a friend class of ExampleClass2,
// its static functions can not gain access to private static
// members of ExampleClass2 directly. Hence the following
// would not work.
//crx_static("ExampleClass2").privateStaticFunction(pExampleClass2);
// Although many solutions exist for this, non of them lead to a
// satisfactory syntax, and hence why there is currently no
// support for that in CrxOop. A work around is to gain
// access to the private static members of ExampleClass2
// using an instance of ExampleClass2.
this.O(pExampleClass2, "ExampleClass2").STATIC.privateStaticFunction(pExampleClass2);
}
}
}
}
});
instance = crx_new('FriendOfExampleClass2');
instance.test1(crx_new('ExampleClass2'));
console.log('------------------');
crx_static('FriendOfExampleClass2').test2(crx_new('ExampleClass2'));
protectedFunction: I am ExampleClass1
protectedFunction: I am ExampleClass2
protectedVirtualFunction: I am ExampleClass1
privateStaticFunction: I am ExampleClass2
protectedFunction: I am ExampleClass1
protectedFunction: I am ExampleClass2
protectedVirtualFunction: I am ExampleClass1
------------------
I am ExampleClass2
protectedFunction: I am ExampleClass1
protectedFunction: I am ExampleClass2
protectedVirtualFunction: I am ExampleClass1
privateStaticFunction: I am ExampleClass2
protectedFunction: I am ExampleClass1
protectedFunction: I am ExampleClass2
protectedVirtualFunction: I am ExampleClass1
crx_registerClass("ExampleClass1",
{
VERBOSE: 1,
'private var privateVar': 'I am ExampleClass1',
'protected function protectedFunction': function()
{
console.log('protectedFunction: ' + this.privateVar);
},
'protected virtual function protectedVirtualFunction': function()
{
console.log('protectedVirtualFunction: ' + this.privateVar);
}
});
crx_registerClass("ExampleClass2",
{
VERBOSE: 1,
EXTENDS: "ExampleClass1",
FRIENDS: ["FriendOfExampleClass2"],
'private var privateVar': 'I am ExampleClass2',
'private function privateFunction': function()
{
console.log('privateFunction: ' + this.privateVar);
},
'private virtual function privateVirtualFunction': function()
{
console.log('privateVirtualFunction: ' + this.privateVar);
},
'private static function privateStaticFunction': function(pExampleClass2)
{
console.log('privateStaticFunction: ' + this.O(pExampleClass2).privateVar);
this.O(pExampleClass2).SR("ExampleClass1", "protectedFunction");
this.O(pExampleClass2).protectedFunction();
this.O(pExampleClass2).protectedVirtualFunction();
},
'protected function protectedFunction': function()
{
console.log('protectedFunction: ' + this.privateVar);
}
});
crx_registerClass("FriendOfExampleClass2",
{
VERBOSE: 1,
'public function test1': function(pExampleClass2)
{
// FriendOfExampleClass2 being a friend class of ExampleClass2,
// its instance functions can gain access to the private memory
// area of instances of ExampleClass2 using 0().
console.log(this.O(pExampleClass2, "ExampleClass2").privateVar);
this.O(pExampleClass2, "ExampleClass2").SR("ExampleClass1", "protectedFunction");
this.O(pExampleClass2, "ExampleClass2").protectedFunction();
this.O(pExampleClass2, "ExampleClass2").protectedVirtualFunction();
// Although FriendOfExampleClass2 is a friend class of ExampleClass2,
// its intance functions can not gain access to private static
// members of ExampleClass2 directly. Hence the following
// would not work.
//crx_static("ExampleClass2").privateStaticFunction(pExampleClass2);
// Although many solutions exist for this, non of them lead to a
// satisfactory syntax, and hence why there is currently no
// support for that in CrxOop. A work around is to gain
// access to the private static members of ExampleClass2
// using an instance of ExampleClass2.
this.O(pExampleClass2, "ExampleClass2").STATIC.privateStaticFunction(pExampleClass2);
},
'public static function test2': function(pExampleClass2)
{
// FriendOfExampleClass2 being a friend class of ExampleClass2,
// its static functions can gain access to the private memory
// area of instances of ExampleClass2 using O().
console.log(this.O(pExampleClass2, "ExampleClass2").privateVar);
this.O(pExampleClass2, "ExampleClass2").SR("ExampleClass1", "protectedFunction");
this.O(pExampleClass2, "ExampleClass2").protectedFunction();
this.O(pExampleClass2, "ExampleClass2").protectedVirtualFunction();
// Although FriendOfExampleClass2 is a friend class of ExampleClass2,
// its static functions can not gain access to private static
// members of ExampleClass2 directly. Hence the following
// would not work.
//crx_static("ExampleClass2").privateStaticFunction(pExampleClass2);
// Although many solutions exist for this, non of them lead to a
// satisfactory syntax, and hence why there is currently no
// support for that in CrxOop. A work around is to gain
// access to the private static members of ExampleClass2
// using an instance of ExampleClass2.
this.O(pExampleClass2, "ExampleClass2").STATIC.privateStaticFunction(pExampleClass2);
}
});
instance = crx_new('FriendOfExampleClass2');
instance.test1(crx_new('ExampleClass2'));
console.log('------------------');
crx_static('FriendOfExampleClass2').test2(crx_new('ExampleClass2'));
protectedFunction: I am ExampleClass1
protectedFunction: I am ExampleClass2
protectedVirtualFunction: I am ExampleClass1
privateStaticFunction: I am ExampleClass2
protectedFunction: I am ExampleClass1
protectedFunction: I am ExampleClass2
protectedVirtualFunction: I am ExampleClass1
------------------
I am ExampleClass2
protectedFunction: I am ExampleClass1
protectedFunction: I am ExampleClass2
protectedVirtualFunction: I am ExampleClass1
privateStaticFunction: I am ExampleClass2
protectedFunction: I am ExampleClass1
protectedFunction: I am ExampleClass2
protectedVirtualFunction: I am ExampleClass1

3.6 Class Keywords

3.6.1 this, THIS
'this' and 'THIS' are perhaps the most important of the class keywords to understand. Internally, a class instance consists of a public memory sector, and a private memory sector. The 'this' keyword, which is the native Javascript 'this' keyword has access to both the public and private memory sectors. 'THIS' on the other hand has access only to the public memory sector. This is why passing 'this' to functions as parameters, or from functions as returns, is dangerous and must never be done. Instead you would pass around 'THIS'.
The same rule apples to 'this' inside static functions, but being static there is no 'THIS' to pass around. Passing 'this' from inside static functions would leak 'this.O' and 'this.STATIC'.
crx_registerClass("ExampleClass",
{
PUBLIC:
{
VARS:
{
"publicVar": 1
},
FUNCTIONS:
{
"publicFunction": function(pExampleClass)
{
// Remember 'THIS' is a class keyword and hence is found on
// 'this' and can be accessed using either 'this':
console.log(this.THIS.publicVar);
// although for clarity you should never use 'THIS' to
// access instance members but instead use
console.log(this.publicVar);
// Or the object that is the class instance:
console.log(pExampleClass.THIS.publicVar);
// but again you should never use 'THIS' to access
// instance members but instead use
console.log(pExampleClass.publicVar);
// 'THIS' is only meant for passing around. When wanting to
// return the instance from functions, instead of the
// dangerous code "return this", you would use
return this.THIS;
},
"publicFunction2": function()
{
// And when wanting to pass the instance to functions,
// instead of passing 'this', you would pass
// "this.THIS" like the following
var vReturn = this.publicFunction(this.THIS);
// Imagine the danger of the following if publicFunction
// above returned 'this' instead of 'THIS'
return vReturn;
}
}
}
});
crx_registerClass("ExampleClass",
{
"VERBOSE": 1,
"public var publicVar": 1,
"public function publicFunction": function(pExampleClass)
{
// Remember 'THIS' is a class keyword and hence is found on
// 'this' and can be accessed using either 'this':
console.log(this.THIS.publicVar);
// although for clarity you should never use 'THIS' to
// access instance members but instead use
console.log(this.publicVar);
// Or the object that is the class instance:
console.log(pExampleClass.THIS.publicVar);
// but again you should never use 'THIS' to access
// instance members but instead use
console.log(pExampleClass.publicVar);
// 'THIS' is only meant for passing around. When wanting to
// return the instance from functions, instead of the
// dangerous code "return this", you would use
return this.THIS;
},
"public function publicFunction2": function()
{
// And when wanting to pass the instance to functions,
// instead of passing 'this', you would pass
// "this.THIS" like the following
var vReturn = this.publicFunction(this.THIS);
// Imagine the danger of the following if publicFunction
// above returned 'this' instead of 'THIS'
return vReturn;
}
});
'this' and 'THIS' have another use in constructors, although this usage should be kept to a minimal. During construction, if you recall, the object is not locked yet and you can continue adding members to it. In the constructor, if you add members to 'this', you create private variables only, even if they are functions. If you add members to 'THIS', you create public variables only, even if they are functions. We only condone, but in rare cases, this usage to create private and public final variables (C++), or const variables (Java). For example:
crx_registerClass("ExampleClass",
{
PUBLIC:
{
CONSTRUCT: function()
{
Object.defineProperty(this, "privateFinalVariable", {"value": 3, "writable": false});
Object.defineProperty(this.THIS, "publicFinalVariable", {"value": 5, "writable": false});
}
}
});
var instance = crx_new("ExampleClass");
console.log(instance.privateFinalVariable);
console.log(instance.publicFinalVariable);
5
crx_registerClass("ExampleClass",
{
"VERBOSE": 1,
"public CONSTRUCT": function()
{
Object.defineProperty(this, "privateFinalVariable", {"value": 3, "writable": false});
Object.defineProperty(this.THIS, "publicFinalVariable", {"value": 5, "writable": false});
}
});
var instance = crx_new("ExampleClass");
console.log(instance.privateFinalVariable);
console.log(instance.publicFinalVariable);
5

3.6.2 O
'O' is a function. While 'this' allows you access to private variables and functions, as well as publics, of the current instance, 'O' allows access to privates of other instances.
'O' also allows friend classes to access the privates of instances of classes be friending said classes. When used this way, 'O' requires two arguments.
Internally 'O' will automatically cast the object to the relevant type and return 'this' of that object. Hence 'O' must never be passed to functions as parameters or from functions as returns, and because 'O' returns 'this', the rule applies to its return too. 'O' returns null if the object can not be casted.
'O' is also available in static functions.
crx_registerClass("ExampleClass1",
{
FRIENDS: ['FriendOfExampleClass1'],
PUBLIC:
{
VARS:
{
"publicVar": "I am ExampleClass1's publicVar"
},
FUNCTIONS:
{
"publicFunction": function(pExampleClass1)
{
// Accessing private members
console.log(this.O(pExampleClass1).privateVar);
// And also publics,
console.log(this.O(pExampleClass1).publicVar);
// but never do this, instead do,
console.log(pExampleClass1.publicVar);
// but the above does not always work because pExampleClass1
// is not guaranteed to be an instance of "ExampleClass1",
// while 'O' automatically casts. Hence if you need
// casting, either continue using 'O' or do
console.log(pExampleClass1.CAST("ExampleClass1").publicVar);
// Like 'this' in the previous section, 'O' must never be
// passed around, but this also applies to the return
// of 'O' which is another 'this'. For example, instead
// of returning this.O(pExampleClass1) to return the
// instance pExampleClass1, do the following
return this.O(pExampleClass1).THIS;
}
},
STATIC:
{
FUNCTIONS:
{
"publicStaticFunction": function(pExampleClass1)
{
// Accessing private members
console.log(this.O(pExampleClass1).privateVar);
// And also publics,
console.log(this.O(pExampleClass1).publicVar);
// but never do this, instead do,
console.log(pExampleClass1.publicVar);
// but the above does not always work because pExampleClass1
// is not guaranteed to be an instance of "ExampleClass1",
// while 'O' automatically casts. Hence if you need
// casting, either continue using 'O' or do
console.log(pExampleClass1.CAST("ExampleClass1").publicVar);
// Like 'this' in the previous section, 'O' must never be
// passed around, but this also applies to the return
// of 'O' which is another 'this'. For example, instead
// of returning this.O(pExampleClass1) to return the
// instance pExampleClass1, do the following
return this.O(pExampleClass1).THIS;
}
}
}
},
PRIVATE:
{
VARS:
{
"privateVar": "privateVar"
}
}
});
crx_registerClass("ExampleClass2",
{
EXTENDS: "ExampleClass1",
PUBLIC:
{
VARS:
{
"publicVar": "I am ExampleClass2's publicVar"
},
FUNCTIONS:
{
"test": function()
{
this.publicFunction(this);
crx_static('ExampleClass1').publicStaticFunction(this);
}
}
}
});
crx_registerClass("FriendOfExampleClass1",
{
PUBLIC:
{
FUNCTIONS:
{
"publicFunction": function(pExampleClass1)
{
// The following is the same as done inside
// ExampleClass1::publicFunction(). Notice how
// 'O' is giving access to the friend class
// FriendOfExampleClass1. Also notice how in this
// case 'O' requires two paramters. Passing only
// the object will not work.
console.log(this.O(pExampleClass1, 'ExampleClass1').privateVar);
console.log(this.O(pExampleClass1, 'ExampleClass1').publicVar);
console.log(pExampleClass1.publicVar);
console.log(pExampleClass1.CAST("ExampleClass1").publicVar);
return this.O(pExampleClass1, 'ExampleClass1').THIS;
}
}
}
});
var instance = crx_new("ExampleClass2");
var instance2 = crx_new("FriendOfExampleClass1");
instance.test();
console.log('--------------------');
instance2.publicFunction(instance.CAST('ExampleClass2'));
I am ExampleClass1's publicVar
I am ExampleClass2's publicVar
I am ExampleClass1's publicVar
privateVar
I am ExampleClass1's publicVar
I am ExampleClass2's publicVar
I am ExampleClass1's publicVar
--------------------
privateVar
I am ExampleClass1's publicVar
I am ExampleClass2's publicVar
I am ExampleClass1's publicVar
crx_registerClass("ExampleClass1",
{
"VERBOSE": 1,
"FRIENDS": ['FriendOfExampleClass1'],
"public var publicVar": "I am ExampleClass1's publicVar",
"public function publicFunction": function(pExampleClass1)
{
// Accessing private members
console.log(this.O(pExampleClass1).privateVar);
// And also publics,
console.log(this.O(pExampleClass1).publicVar);
// but never do this, instead do,
console.log(pExampleClass1.publicVar);
// but the above does not always work because pExampleClass1
// is not guaranteed to be an instance of "ExampleClass1",
// while 'O' automatically casts. Hence if you need
// casting, either continue using 'O' or do
console.log(pExampleClass1.CAST("ExampleClass1").publicVar);
// Like 'this' in the previous section, 'O' must never be
// passed around, but this also applies to the return
// of 'O' which is another 'this'. For example, instead
// of returning this.O(pExampleClass1) to return the
// instance pExampleClass1, do the following
return this.O(pExampleClass1).THIS;
},
"public static function publicStaticFunction": function(pExampleClass1)
{
// Accessing private members
console.log(this.O(pExampleClass1).privateVar);
// And also publics,
console.log(this.O(pExampleClass1).publicVar);
// but never do this, instead do,
console.log(pExampleClass1.publicVar);
// but the above does not always work because pExampleClass1
// is not guaranteed to be an instance of "ExampleClass1",
// while 'O' automatically casts. Hence if you need
// casting, either continue using 'O' or do
console.log(pExampleClass1.CAST("ExampleClass1").publicVar);
// Like 'this' in the previous section, 'O' must never be
// passed around, but this also applies to the return
// of 'O' which is another 'this'. For example, instead
// of returning this.O(pExampleClass1) to return the
// instance pExampleClass1, do the following
return this.O(pExampleClass1).THIS;
},
"private var privateVar": "privateVar"
});
crx_registerClass("ExampleClass2",
{
"VERBOSE": 1,
"extends": "ExampleClass1",
"public var publicVar": "I am ExampleClass2's publicVar",
"public function test": function()
{
this.publicFunction(this);
crx_static('ExampleClass1').publicStaticFunction(this);
}
});
crx_registerClass("FriendOfExampleClass1",
{
"VERBOSE": 1,
"public function publicFunction": function(pExampleClass1)
{
// The following is the same as done inside
// ExampleClass1::publicFunction(). Notice how
// 'O' is giving access to the friend class
// FriendOfExampleClass1. Also notice how in this
// case 'O' requires two paramters. Passing only
// the object will not work.
console.log(this.O(pExampleClass1, 'ExampleClass1').privateVar);
console.log(this.O(pExampleClass1, 'ExampleClass1').publicVar);
console.log(pExampleClass1.publicVar);
console.log(pExampleClass1.CAST("ExampleClass1").publicVar);
return this.O(pExampleClass1, 'ExampleClass1').THIS;
}
});
var instance = crx_new("ExampleClass2");
var instance2 = crx_new("FriendOfExampleClass1");
instance.test();
console.log('--------------------');
instance2.publicFunction(instance.CAST('ExampleClass2'));
I am ExampleClass1's publicVar
I am ExampleClass2's publicVar
I am ExampleClass1's publicVar
privateVar
I am ExampleClass1's publicVar
I am ExampleClass2's publicVar
I am ExampleClass1's publicVar
--------------------
privateVar
I am ExampleClass1's publicVar
I am ExampleClass2's publicVar
I am ExampleClass1's publicVar

3.6.3 CAST
CAST is a function. It allows you to upcast or downcast an instance to other classes of the extension chain. 'CAST' must never be passed to functions as parameters or from functions as returns, and there can never be a need to do so. Misuse of this keyword can lead to fatal errors.
crx_registerClass("ExampleClass1",
{
PUBLIC:
{
VARS:
{
"publicVar": "I am ExampleClass1's publicVar"
},
FUNCTIONS:
{
"publicFunction": function(pExampleClass2)
{
// Casting to access pExampleClass2's
// ExampleClass1::publicVar variable
console.log(pExampleClass2.CAST("ExampleClass1").publicVar);
// But casting to access ExampleClass1::privateVar
// will not work.
console.log(pExampleClass2.CAST("ExampleClass1").privateVar);
// However, because we are in the same class that we are
// casting to, we can use 'O', which can be used
// to access publics and privates.
console.log(this.O(pExampleClass2).privateVar);
}
}
},
PRIVATE:
{
VARS:
{
"privateVar": "privateVar"
}
}
});
crx_registerClass("ExampleClass2",
{
EXTENDS: "ExampleClass1",
PUBLIC:
{
VARS:
{
"publicVar": "I am ExampleClass2's publicVar"
},
FUNCTIONS:
{
"publicFunction": function()
{
console.log(this.publicVar);
// And remember CAST is a class keyword, hence it is
// found on 'this' as well.
console.log(this.CAST("ExampleClass1").publicVar);
}
}
}
});
var instance = crx_new("ExampleClass2");
// Casting to call ExampleClass1::publicFunction, but remember
// this will not work for virtual functions.
instance.CAST("ExampleClass1").publicFunction(instance);
console.log("------------");
instance.publicFunction();
undefined
privateVar
------------
I am ExampleClass2's publicVar
I am ExampleClass1's publicVar
crx_registerClass("ExampleClass1",
{
"VERBOSE": 1,
"public var publicVar": "I am ExampleClass1's publicVar",
"public function publicFunction": function(pExampleClass2)
{
// Casting to access pExampleClass2's
// ExampleClass1::publicVar variable
console.log(pExampleClass2.CAST("ExampleClass1").publicVar);
// But casting to access ExampleClass1::privateVar
// will not work.
console.log(pExampleClass2.CAST("ExampleClass1").privateVar);
// However, because we are in the same class that we are
// casting to, we can use 'O', which can be used
// to access publics and privates.
console.log(this.O(pExampleClass2).privateVar);
},
"private var privateVar": "privateVar"
});
crx_registerClass("ExampleClass2",
{
"VERBOSE": 1,
"extends": "ExampleClass1",
"public var publicVar": "I am ExampleClass2's publicVar",
"public function publicFunction": function()
{
console.log(this.publicVar);
// And remember CAST is a class keyword, hence it is
// found on 'this' as well.
console.log(this.CAST("ExampleClass1").publicVar);
}
});
var instance = crx_new("ExampleClass2");
// Casting to call ExampleClass1::publicFunction, but remember
// this will not work for virtual functions.
instance.CAST("ExampleClass1").publicFunction(instance);
console.log("------------");
instance.publicFunction();
undefined
privateVar
------------
I am ExampleClass2's publicVar
I am ExampleClass1's publicVar

3.6.4 PARENT
The class keyword PARENT is equivalent to casting 'this' to the parent class using 'CAST'. However, 'PARENT' is faster than 'CAST' and more readable. 'PARENT', unlike 'this' for example, is safe to pass to and from functions.
crx_registerClass("ExampleClass1",
{
PUBLIC:
{
VARS:
{
"publicVar": "I am ExampleClass1's publicVar"
}
}
});
crx_registerClass("ExampleClass2",
{
EXTENDS: "ExampleClass1",
PUBLIC:
{
VARS:
{
"publicVar": "I am ExampleClass2's publicVar"
},
FUNCTIONS:
{
"publicFunction": function()
{
console.log(this.publicVar);
// Printing "publicVar" of parent class
console.log(this.PARENT.publicVar);
// Which is equivalent to
console.log(this.CAST("ExampleClass1").publicVar);
}
}
}
});
var instance = crx_new("ExampleClass2");
instance.publicFunction();
I am ExampleClass1's publicVar
I am ExampleClass1's publicVar
crx_registerClass("ExampleClass1",
{
"VERBOSE": 1,
"public var publicVar": "I am ExampleClass1's publicVar"
});
crx_registerClass("ExampleClass2",
{
"VERBOSE": 1,
"extends": "ExampleClass1",
"public var publicVar": "I am ExampleClass2's publicVar",
"public function publicFunction": function()
{
console.log(this.publicVar);
// Printing "publicVar" of parent class
console.log(this.PARENT.publicVar);
// Which is equivalent to
console.log(this.CAST("ExampleClass1").publicVar);
}
});
var instance = crx_new("ExampleClass2");
instance.publicFunction();
I am ExampleClass1's publicVar
I am ExampleClass1's publicVar

3.6.5 CONSTRUCT
'CONSTRUCT' is a function. The class keyword 'CONSTRUCT' is not to be confused with the definition keyword 'CONSTRUCT'. 'CONSTRUCT' simply points to the constructor in the class definition, and is only available during construction. The only valid use of CONSTRUCT is to call the parent constructor using "this.PARENT.CONSTRUCT()" when an explicit call is needed. Never pass "CONSTRUCT" to functions as parameters, or from functions as returns.
crx_registerClass("ExampleClass1",
{
PUBLIC:
{
CONSTRUCT: function(pValue)
{
if(pValue !== undefined)
{this.privateVar = pValue;}
},
FUNCTIONS:
{
'test': function()
{
console.log(this.privateVar);
}
}
},
PRIVATE:
{
VARS:
{
"privateVar": "I am default privateVar"
}
}
});
crx_registerClass("ExampleClass2",
{
EXTENDS: "ExampleClass1",
PUBLIC:
{
CONSTRUCT: function(pValue)
{
// Calling the parent class's constructor explicitly to
// pass the necessary parameters
this.PARENT.CONSTRUCT(pValue);
}
}
});
crx_registerClass("ExampleClass3",
{
EXTENDS: "ExampleClass2",
PUBLIC:
{
CONSTRUCT: function(pValue)
{
// Calling the parent class's constructor explicitly to
// pass the necessary parameters
this.PARENT.CONSTRUCT(pValue);
// The following are examples of what would lead to
// fatal errors:
// - RECURSIVE CALL TO CONSTRUCTOR
// this.CONSTRUCT(pValue);
// - SECOND CALL TO PARENT's CONSTRUCTOR
// this.PARENT.CONSTRUCT(pValue);
// - CALL TO GRAND PARENT's CONSTRUCTOR
// this.PARENT.PARENT.CONSTRUCT(pValue);
}
}
});
var instance = crx_new("ExampleClass3", "I am a new privateVar");
instance.test();
crx_registerClass("ExampleClass1",
{
"VERBOSE": 1,
"public CONSTRUCT": function(pValue)
{
if(pValue !== undefined)
{this.privateVar = pValue;}
},
"public function test": function()
{
console.log(this.privateVar);
},
"private var privateVar": "I am default privateVar"
});
crx_registerClass("ExampleClass2",
{
"VERBOSE": 1,
"extends": "ExampleClass1",
"public CONSTRUCT": function(pValue)
{
// Calling the parent class's constructor explicitly to
// pass the necessary parameters
this.PARENT.CONSTRUCT(pValue);
}
});
crx_registerClass("ExampleClass3",
{
"VERBOSE": 1,
"extends": "ExampleClass2",
"public CONSTRUCT": function(pValue)
{
// Calling the parent class's constructor explicitly to
// pass the necessary parameters
this.PARENT.CONSTRUCT(pValue);
// The following are examples of what would lead to
// fatal errors:
// - RECURSIVE CALL TO CONSTRUCTOR
// this.CONSTRUCT(pValue);
// - SECOND CALL TO PARENT's CONSTRUCTOR
// this.PARENT.CONSTRUCT(pValue);
// - CALL TO GRAND PARENT's CONSTRUCTOR
// this.PARENT.PARENT.CONSTRUCT(pValue);
}
});
var instance = crx_new("ExampleClass3", "I am a new privateVar");
instance.test();

3.6.6 STATIC
'STATIC' is used to access the static members of a class. Internaly, on class instances only, two 'STATIC's exist, one in the private memory sector, and one in the public memory sector, and they are accessed using "this.STATIC" and "this.THIS.STATIC". Unlike "this.THIS.STATIC", "this.STATIC" is unsafe to pass to functions as parameters or from functions as returns.
In static functions, only "this.STATIC" exists, and it is also unsafe to pass to functions as parameters or from functions as returns.
crx_registerClass("ExampleClass",
{
PUBLIC:
{
CONSTS:
{
"publicConstant": 1
},
STATIC:
{
VARS:
{
"publicStaticVar": 4
},
FUNCTIONS:
{
"test2": function()
{
// Accessing a public static member and a public constant
console.log(this.STATIC.publicStaticVar);
console.log(this.STATIC.publicConstant);
// and we could have done this
console.log(crx_static("ExampleClass").publicStaticVar);
console.log(crx_static("ExampleClass").publicConstant);
// Accessing a private static member and a private constant
console.log(this.STATIC.privateStaticVar);
console.log(this.STATIC.privateConstant);
// but unlike the public accessor case, the following
// will not work.
console.log(crx_static("ExampleClass").privateStaticVar);
console.log(crx_static("ExampleClass").privateConstant);
}
}
},
FUNCTIONS:
{
'test': function()
{
// Accessing a public static member and a public constant
console.log(this.STATIC.publicStaticVar);
console.log(this.STATIC.publicConstant);
// and we could have done this
console.log(crx_static("ExampleClass").publicStaticVar);
console.log(crx_static("ExampleClass").publicConstant);
// Accessing a private static member and a private constant
console.log(this.STATIC.privateStaticVar);
console.log(this.STATIC.privateConstant);
// but unlike the public accessor case, the following
// will not work.
console.log(crx_static("ExampleClass").privateStaticVar);
console.log(crx_static("ExampleClass").privateConstant);
}
}
},
PRIVATE:
{
CONSTS:
{
"privateConstant": 2
},
STATIC:
{
VARS:
{
"privateStaticVar": 5
}
}
}
});
var instance = crx_new("ExampleClass");
instance.test();
console.log('------------------');
console.log('DOING TESTS FROM WITHIN A STATIC FUNCTION');
crx_static('ExampleClass').test2();
1
4
1
5
2
undefined
undefined
------------------
DOING TESTS FROM WITHIN A STATIC FUNCTION
4
1
4
1
5
2
undefined
undefined
crx_registerClass("ExampleClass",
{
"VERBOSE": 1,
"public const publicConstant": 1,
"public static var publicStaticVar": 4,
"public static function test2": function()
{
// Accessing a public static member and a public constant
console.log(this.STATIC.publicStaticVar);
console.log(this.STATIC.publicConstant);
// and we could have done this
console.log(crx_static("ExampleClass").publicStaticVar);
console.log(crx_static("ExampleClass").publicConstant);
// Accessing a private static member and a private constant
console.log(this.STATIC.privateStaticVar);
console.log(this.STATIC.privateConstant);
// but unlike the public accessor case, the following
// will not work.
console.log(crx_static("ExampleClass").privateStaticVar);
console.log(crx_static("ExampleClass").privateConstant);
},
"public function test": function()
{
// Accessing a public static member and a public constant
console.log(this.STATIC.publicStaticVar);
console.log(this.STATIC.publicConstant);
// and we could have done this
console.log(crx_static("ExampleClass").publicStaticVar);
console.log(crx_static("ExampleClass").publicConstant);
// Accessing a private static member and a private constant
console.log(this.STATIC.privateStaticVar);
console.log(this.STATIC.privateConstant);
// but unlike the public accessor case, the following
// will not work.
console.log(crx_static("ExampleClass").privateStaticVar);
console.log(crx_static("ExampleClass").privateConstant);
},
"private const privateConstant": 2,
"private static var privateStaticVar": 5
});
var instance = crx_new("ExampleClass");
instance.test();
console.log('------------------');
console.log('DOING TESTS FROM WITHIN A STATIC FUNCTION');
crx_static('ExampleClass').test2();
1
4
1
5
2
undefined
undefined
------------------
DOING TESTS FROM WITHIN A STATIC FUNCTION
4
1
4
1
5
2
undefined
undefined

3.6.7 SR
"SR" stands for Scope Resolver and is roughly the equivilant of "::" in C++. However, it can only be used on instances, and up the extension chain. It can be used to access virtual, and non virtual, non static, members of the class. Its use should generally be kept to a minimum, but is provided here for when needed.
"SR", like "O" for example, must never be passed to functions as parameters, or returned from functions as returns. "SR" will cause fatal errors if the class is not in the instance's class extension chain at all, or if misused.
crx_registerClass("ExampleClass1",
{
PUBLIC:
{
VARS:
{
"publicVar": "I am ExampleClass1",
},
VIRTUAL:
{
FUNCTIONS:
{
"pulicVirtualFunction": function(pA)
{
console.log(this.publicVar);
}
}
},
FUNCTIONS:
{
"pulicFunction": function(pA)
{
console.log("From ExampleClass1::publicFunction: " + this.publicVar);
}
}
}
});
crx_registerClass("ExampleClass2",
{
EXTENDS: "ExampleClass1",
PUBLIC:
{
VARS:
{
"publicVar": "I am ExampleClass2",
},
VIRTUAL:
{
FUNCTIONS:
{
"pulicVirtualFunction": function(pA)
{
console.log(this.publicVar);
}
}
},
FUNCTIONS:
{
"pulicFunction": function(pA)
{
console.log("From ExampleClass2::publicFunction: " + this.publicVar);
}
}
}
});
crx_registerClass("ExampleClass3",
{
EXTENDS: "ExampleClass2",
PUBLIC:
{
VARS:
{
"publicVar": "I am ExampleClass3",
},
VIRTUAL:
{
FUNCTIONS:
{
"pulicVirtualFunction": function(pA)
{
console.log(this.publicVar);
}
}
},
FUNCTIONS:
{
test: function(pA)
{
// Calling ExampleClass1::pulicVirtualFunction
// using CAST will not work with virtual
// functions.
this.CAST("ExampleClass1").pulicVirtualFunction(5);
// Instead we have to use 'SR'
this.SR("ExampleClass1", "pulicVirtualFunction", 5);
// The following would call
// ExampleClass2::pulicVirtualFunction
this.SR("ExampleClass2", "pulicVirtualFunction", 5);
// The following is equivalent to the above because 'null'
// automatically resolves to the parent of the instance's
// class, which in the case of 'this' is ExampleClass2.
this.SR(null, "pulicVirtualFunction", 5);
// Trying to call ExampleClass1::pulicFunction using the following
// will not work because of name hiding caused by the existance
// of ExampleClass2::pulicFunction
this.pulicFunction(5);
// Instead we have to use 'SR'
this.SR("ExampleClass1", "pulicFunction", 5);
// The same problem exists for ExampleClass1::publicVar, and hence the
// the following will not work, reading and writing to
// ExampleClass2::publicVar instead.
console.log(this.publicVar);
this.publicVar = "FIRST CHANGE";
// Instead we have to use 'SR'
console.log(this.SR("ExampleClass1", "publicVar"));
this.SR("ExampleClass1", "publicVar", "SECOND CHANGE");
// Printing the result of the above
console.log("------------------");
console.log(this.publicVar);
console.log(this.SR("ExampleClass2", "publicVar"));
console.log(this.SR("ExampleClass1", "publicVar"));
}
}
}
});
var instance = crx_new("ExampleClass3");
instance.test();
//SR is also available on the instance
console.log("------------------");
instance.SR("ExampleClass1", "pulicVirtualFunction", 5);
I am ExampleClass1
I am ExampleClass2
I am ExampleClass2
From ExampleClass2::publicFunction: I am ExampleClass2
From ExampleClass1::publicFunction: I am ExampleClass1
I am ExampleClass3
I am ExampleClass1
------------------
FIRST CHANGE
I am ExampleClass2
SECOND CHANGE
------------------
SECOND CHANGE
crx_registerClass("ExampleClass1",
{
"VERBOSE": 1,
"public var publicVar": "I am ExampleClass1",
"public virtual function pulicVirtualFunction": function(pA)
{
console.log(this.publicVar);
},
"public function pulicFunction": function(pA)
{
console.log("From ExampleClass1::publicFunction: " + this.publicVar);
}
});
crx_registerClass("ExampleClass2",
{
"VERBOSE": 1,
"extends": "ExampleClass1",
"public var publicVar": "I am ExampleClass2",
"public virtual function pulicVirtualFunction": function(pA)
{
console.log(this.publicVar);
},
"public function pulicFunction": function(pA)
{
console.log("From ExampleClass2::publicFunction: " + this.publicVar);
}
});
crx_registerClass("ExampleClass3",
{
"VERBOSE": 1,
"extends": "ExampleClass2",
"public var publicVar": "I am ExampleClass3",
"public virtual function pulicVirtualFunction": function(pA)
{
console.log(this.publicVar);
},
"public function test": function(pA)
{
// Calling ExampleClass1::pulicVirtualFunction
// using CAST will not work with virtual
// functions.
this.CAST("ExampleClass1").pulicVirtualFunction(5);
// Instead we have to use 'SR'
this.SR("ExampleClass1", "pulicVirtualFunction", 5);
// The following would call
// ExampleClass2::pulicVirtualFunction
this.SR("ExampleClass2", "pulicVirtualFunction", 5);
// The following is equivalent to the above because 'null'
// automatically resolves to the parent of the instance's
// class, which in the case of 'this' is ExampleClass2.
this.SR(null, "pulicVirtualFunction", 5);
// Trying to call ExampleClass1::pulicFunction using the following
// will not work because of name hiding caused by the existance
// of ExampleClass2::pulicFunction
this.pulicFunction(5);
// Instead we have to use 'SR'
this.SR("ExampleClass1", "pulicFunction", 5);
// The same problem exists for ExampleClass1::publicVar, and hence the
// the following will not work, reading and writing to
// ExampleClass2::publicVar instead.
console.log(this.publicVar);
this.publicVar = "FIRST CHANGE";
// Instead we have to use 'SR'
console.log(this.SR("ExampleClass1", "publicVar"));
this.SR("ExampleClass1", "publicVar", "SECOND CHANGE");
// Printing the result of the above
console.log("------------------");
console.log(this.publicVar);
console.log(this.SR("ExampleClass2", "publicVar"));
console.log(this.SR("ExampleClass1", "publicVar"));
}
});
var instance = crx_new("ExampleClass3");
instance.test();
//SR is also available on the instance
console.log("------------------");
instance.SR("ExampleClass1", "pulicVirtualFunction", 5);
I am ExampleClass1
I am ExampleClass2
I am ExampleClass2
From ExampleClass2::publicFunction: I am ExampleClass2
From ExampleClass1::publicFunction: I am ExampleClass1
I am ExampleClass3
I am ExampleClass1
------------------
FIRST CHANGE
I am ExampleClass2
SECOND CHANGE
------------------
SECOND CHANGE

3.6.8 ANNUL
ANNUL is a function. For various reasons CrxOop does not give an implementation for destructors. After contemplating many ideas, ANNUL was invented. ANNUL should allow developers to provide their own destructor mechanisms should they need to.
If this.ANNUL() is called, the object pointed to by 'this' is locked. Any further access to the object members will lead to fatal errors. 'ANNUL' must never be passed to functions as parameters or from functions as returns. Misuse of this keyword can also lead to fatal errors.
crxOop.isAnnul() can be used to check whether an object has been annuled.
The following is an example of how a destructor mechanism could be implemented using ANNUL.
(function()
{
var instances = {};
var id = 1;
// A base class for all our classes that follow the destructable class pattern.
crx_registerClass('Base',
{
PRIVATE:
{
VARS:
{
'id': 0
}
},
PUBLIC:
{
CONSTRUCT: function()
{
// Saving a reference of the instance into our private
// local closure. Why one might need to do this
// is beside the point. Assuming you do need to
// do this, you need to worry about freeing the
// memory when the object is no longer
// needed. Look at our 'destruct' function below.
this.id = id;
instances[id] = this.THIS;
id = id + 1;
},
VIRTUAL:
{
FUNCTIONS:
{
// Our destructor function.
'destruct': function()
{
// The following will not delete the instance.
// There might be other references to it
// elsewhere.
delete instances[this.id];
// The following will also not delete the instance.
// However, now the instance is roughly as good
// as deleted. Any use of this instance later
// on will cause fatal errors. This will allow
// developers to see these errors before their
// code is released.
this.ANNUL();
}
}
}
}
});
})();
crx_registerClass('SomeClass',
{
EXTENDS: 'Base',
PRIVATE:
{
VARS:
{
'someString': null
}
},
PUBLIC:
{
FUNCTIONS:
{
'setString': function(pString)
{
this.someString = pString;
},
'doSomething': function()
{
console.log(this.someString);
}
},
VIRTUAL:
{
FUNCTIONS:
{
// Our destructor function.
'destruct': function()
{
// Freeing our resource
this.someString = null;
// Allowing upper classes to free their resources.
// Notice how we do not call 'ANNUL' here.
// This is because 'ANNUL' must be called as the
// last line to avoid errors from further access
// to the object. This means, the last line in
// the last destruct function to be called,
// which is Base::destruct()
this.SR(null, 'destruct');
}
}
}
}
});
var a = crx_new('SomeClass');
a.setString('Some very large resource');
a.doSomething();
// We are now done.
a.destruct();
// The following will cause a fatal error.
//a.doSomething()
// The following will not. 'crxOop.isAnnul' can be used to check if the object has been
// annuled, which in our case is true.
if(!crxOop.isAnnul(a))
{
a.doSomething();
}
(function()
{
var instances = {};
var id = 1;
// A base class for all our classes that follow the destructable class pattern.
crx_registerClass('Base',
{
'VERBOSE': 1,
'private var id': 0,
'public CONSTRUCT': function()
{
// Saving a reference of the instance into our private
// local closure. Why one might need to do this
// is beside the point. Assuming you do need to
// do this, you need to worry about freeing the
// memory when the object is no longer
// needed. Look at our 'destruct' function below.
this.id = id;
instances[id] = this.THIS;
id = id + 1;
},
// Our destructor function.
'public virtual function destruct': function()
{
// The following will not delete the instance.
// There might be other references to it
// elsewhere.
delete instances[this.id];
// The following will also not delete the instance.
// However, now the instance is roughly as good
// as deleted. Any use of this instance later
// on will cause fatal errors. This will allow
// developers to see these errors before their
// code is released.
this.ANNUL();
}
});
})();
crx_registerClass('SomeClass',
{
'VERBOSE': 1,
'extends': 'Base',
'private var someString': null
'public function setString': function(pString)
{
this.someString = pString;
},
'public function doSomething': function()
{
console.log(this.someString);
},
// Our destructor function.
'public virtual function destruct': function()
{
// Freeing our resource
this.someString = null;
// Allowing upper classes to free their resources.
// Notice how we do not call 'ANNUL' here.
// This is because 'ANNUL' must be called as the
// last line to avoid errors from further access
// to the object. This means, the last line in
// the last destruct function to be called,
// which is Base::destruct
this.SR(null, 'destruct');
}
});
var a = crx_new('SomeClass');
a.setString('Some very large resource');
a.doSomething();
// We are now done.
a.destruct();
// The following will cause a fatal error.
//a.doSomething()
// The following will not. 'crxOop.isAnnul' can be used
// to check if the object has been annuled, which
// in our case is true.
if(!crxOop.isAnnul(a))
{
a.doSomething();
}

4.0 Interfaces
Initially, in v1.0 of CrxOop, CrxOop did not allow the creation of pure virtual methods, while at the same time the Javscript technology does not allow a satisfactory implementation of multiple inheritance for classes. This leads to the implementation of what are called interfaces in Java. In C++, these would be classes with only public pure virtual methods.
Note the following:
- Interfaces do not define variables
- Interfaces do not define full function signatures due to Javascript's limitations, only function names
- Interface supports multiple inheritance to other interfaces
- Interface can not be instantiated
- A class may implement multiple interfaces
- A class implements an interface by defining a public virtual function, or a public pure virtual function with the same name as the name declared in the interface definition, and doing this for all functions declared in the interface definition.
- Although virtual functions may have different accessors set going down the class extension chain, once a virtual function is required by an interface, pertaining classes in the class extension chain may only define said virtual function as public.
- Class instances can not be cast to interfaces
- Interfaces are still a type, and class instances can be checked against them

4.1 Definition and Registration
Before interfaces can be extended by other classes they have to be defined. During definition write up, you will encounter syntax errors, as with every other code you write in javascript. After fixing the syntax, you will likely encounter Definition Errors, which are CrxOop's equivalent of javascript syntax errors. These must also be fixed before anything happens. Please refer to the section on errors for more information.
After a definition, an interface can either be resgistered explicitly using crx_registerInterface() / crxOop.crx_registerInterface() which would allow you to give it a name, or registered implicitly during calls to other parts of the library, such as crx_new.
Explicit registration is the recommended way of registration, and you can either do it by assigning a definition to a variable and then calling crx_registerInterface(). In this case the interface can be refered to using the variable name of the definition, or the registered interface name.
var InterfaceDefinition =
{
//DEFINITION
};
crx_registerInterface("myNameSpace.myInterface", InterfaceDefinition);
var ClassDefinition =
{
IMPLEMENTS: [InterfaceDefinition]
//REST OF DEFINITION
}
//OR
var ClassDefinition =
{
IMPLEMENTS: ['myNameSpace.myInterface']
//REST OF DEFINITION
}
Or by passing the definition immediately to crx_registerInterface(), which is our prefered approach:
crx_registerInterface("myNameSpace.myInterface",
{
//DEFINITION
});
Note that there is no actual support for name spaces. The full string "myNameSpace.myInterface" is the name of the interface, and not just "myInterface". However the use of ".", or something similar, is useful to avoid name collisions. Also note that interface names can collide with class names and vice versa. Explicit registration is very useful when it comes to definition errors.
The following is an example of implicit registration:
var InterfaceDefinition =
{
//DEFINITION
};
var ClassDefinition =
{
IMPLEMENTS: [InterfaceDefinition]
//REST OF DEFINITION
}
var instance1 = crx_new(ClassDefinition);
Note that interfaces registered implicitly can not be re registered explicitly later on. Also note that interfaces that are not registered implicitly or explicitly do not exist as far as CrxOop is concerned until they are registered. This is important to keep in mind when encountering errors about missing definitions.

4.2 Syntax
Interfaces are defined using plain objects where the keys define the names of functions.
crx_registerInterface("myNameSpace.myInterface",
{
INHERITS: ["myNameSpace.someOtherInterface1", "myNameSpace.someOtherInterface2"],
"function1": 0,
"function2": 0
});
The above interface declares two methods to be implemented by implementing classes along with the methods declared by the two interfaces it inherits, "myNameSpace.someOtherInterface1" and "myNameSpace.someOtherInterface2". An interface may inherit one or more other interfaces, using the definition keyword 'INHERITS'. Interfaces may not inherit other interfaces defining functions with the same name. The same applies for classes, classes may not implement multiple interfaces such as one of the interfaces that it implements defines a method already defined in another interface that it implements.
Classes may implement interfaces using the definition keyword 'INHERITS' as shown below.
crx_registerInterface("ExampleInterface1",
{
"function1": 0,
"function2": 0
});
crx_registerInterface("ExampleInterface2",
{
INHERITS: ["ExampleInterface1"],
"function3": 0
});
crx_registerInterface("ExampleInterface3",
{
"function4": 0
});
crx_registerClass("ExampleClass1",
{
PUBLIC:
{
VIRTUAL:
{
FUNCTIONS:
{
"function1": function(){}
}
}
}
});
crx_registerClass("ExampleClass2",
{
IMPLEMENTS: ["ExampleInterface2", "ExampleInterface3"],
EXTENDS: "ExampleClass1",
PUBLIC:
{
VIRTUAL:
{
FUNCTIONS:
{
"function2": function(){},
"function3": function(){},
"function4": function(){}
}
}
}
});
var instance = crx_new("ExampleClass2");
crx_registerInterface("ExampleInterface1",
{
"function1": 0,
"function2": 0
});
crx_registerInterface("ExampleInterface2",
{
INHERITS: ["ExampleInterface1"],
"function3": 0
});
crx_registerInterface("ExampleInterface3",
{
"function4": 0
});
crx_registerClass("ExampleClass1",
{
"VERBOSE": 1,
"public virtual function function1": function(){}
});
crx_registerClass("ExampleClass2",
{
"VERBOSE": 1,
"implements": ["ExampleInterface2", "ExampleInterface3"],
"extends": "ExampleClass1",
"public virtual function function2": function(){},
"public virtual function function3": function(){},
"public virtual function function4": function(){}
});
var instance = crx_new("ExampleClass2");
Notice how the class "ExampleClass2" did not need to implement "function1" defined in "ExampleInterface1". This is because "function" is already defined by another class in its extension chain. However, CrxOop will issue a warning.

5.0 Structures

5.1 Introduction
Structures are this author's vision, formalation, and generalization of prototypal inheritance and how it would work if javascript was a non scripting for rapid development, strict language like C++ or Java. Structure are CrxOop's implementation of Prototype Based Programming, or as this author likes to call it, Prototype Object Based Programming (POBP).
Some history. CrxOop started out as a proof of concept. It was meant to prove that a sufficiently performant implementation of object oriented programming could be added to current javscript engines (browsers) without much difficulty. Later CrxOop grew beyond that to provide full Object Oriented capabilities with Javascript Itself. Classes were meant to provide a facility for developing complex algorithms, and were not suited for data structures due to their large resource consumption. The underlying javascript plain objects were superior for the task, and a simple function to create an object and attach the required fields was always going to be the more efficient solution for data structures.
As it were, Javascript also provides prototypes, and instead one could use constructor functions to fill objects with data and benefit from the added benefits of prototypal inheritance. These benefits, without being stretched, were very limited this author thinks. Stretching, gave rise to different approaches and code recipes to try and mimick concepts from OOP. Code often looked messy. With the strange syntax that prototypes required, and the different code recipes used by developers to mimick things like private variables, code often looked confusing as well. Some developers were resulting to using underscores below variables or functions names to indicate they were private. An unspoken tradition, that is not always possible to tell whether the code is following it or something else.
One of the most important benefits of OOP, this author thinks, is that the code documents itself. With the need for data structures, a new mechanism had to be developed, and since with javascript it is already very easy to built arbitrary objects, this mechanism had to rival the underlying javascript mechanism. It had to reduce to it. In other words, be a super set of it. It had to be efficient. It also had to give the same aformentioned benefits from OOP. This gave rise to Structures.

5.2 Instantiation
The equivalent of the "new" keyword that is found in many programing languages is the function
crx_new() / crxOop.crx_new(). The function works the same way as it does for classes, and has the same
exact signature. It is the only way to create structure instances, and provides four overrides
which allow the creation of single or multiple instances. All four overrides allow the use of
anonymous structure definitions,
a feature which can be abused very easily.
crx_registerStructure("MyStructure",
{
"VERBOSE": 1,
"public CONSTRUCT": function(pA, pB)
{
console.log("(" + pA + "," + pB + ")");
}
});
var gMyStructure = crx_new("MyStructure", 1, 1);
crx_registerStructure("MyStructure",
{
"VERBOSE": 1,
"public CONSTRUCT": function(pA, pB)
{
console.log("(" + pA + "," + pB + ")");
}
});
var gMyStructure = crx_new(2, "MyStructure", 1, 1);
(1,1)
crx_registerStructure("MyStructure",
{
"VERBOSE": 1,
"public CONSTRUCT": function(pA, pB)
{
console.log("(" + pA + "," + pB + ")");
}
});
var gMyStructure = crx_new(4, [[1,1], [2,2]], "MyStructure");
(2,2)
(2,2)
(2,2)
crx_registerStructure("MyStructure",
{
"VERBOSE": 1,
"public CONSTRUCT": function(pA, pB)
{
console.log("(" + pA + "," + pB + ")");
}
});
var gMyStructure = crx_new(4, function(pIndex)
{
return [pIndex, pIndex * pIndex];
}, "MyStructure");
(1,1)
(2,4)
(3,9)

5.3 Definition and Registration
Before structure instances can be made, they must be defined using either of the two syntaxes mentioned in the introduction. Definitions must not be created or altered using calles to defineProperty(), seal() or other similar methods.
During definition write up, you will encounter syntax errors, as with every other code you write in javascript. After fixing the syntax, you will likely encounter Definition Errors, which are CrxOop's equivalent of javascript syntax errors. These must also be fixed before anything happens. Please refer to the section on errors for more information.
After a definition, a structure can either be resgistered explicitly using crx_registerStructure() which would allow you to give it a name, or registered implicitly during calls to other parts of the library, such as crx_new().
Explicit registration is the recommended way of registration, and you can either do it by assigning a definition to a variable and then calling crx_registerStructure(). In this case instances can be created using the variable name of the definition, or the registered class name.
var structureDefinition =
{
//DEFINITION
};
crx_registerStructure("myNameSpace.myStructure", structureDefinition);
var instance1 = crx_new(structureDefinition);
//OR
var instance2 = crx_new("myNameSpace.myStructure");
Or by passing the definition immediately to crx_registerStructure(), which is our prefered approach:
crx_registerStructure("myNameSpace.myStructure",
{
//DEFINITION
});
var instance2 = crx_new("myNameSpace.myStructure");
Note that there is no actual support for name spaces. The full string "myNameSpace.myStructure" is the name of the structure, and not just "myStructure". However the use of ".", or something similar, is useful to avoid name collisions. Also note that structure names can collide with class names and interface names and vice versa. Explicit registration is very useful when it comes to definition errors.
The following is an example of implicit registration:
var structureDefinition =
{
//DEFINITION
};
var instance1 = crx_new(structureDefinition);
Note that structures registered implicitly can not be re registered explicitly later on. Also note that structures that are not registered implicitly or explicitly do not exist as far as CrxOop is concerned until they are registered. This is important to keep in mind when encountering errors about missing definitions.
With implicit registration, one can define anonymous structures
var instance1 = crx_new(
{
//DEFINITION
});
The above can be convenient, however beware. Every such call initiates an internal structures registration, which coupled with the immediate need to build an instance, leads to parsing which means resource consumption. If you need to make more than one instance of a structures, do not declare it anonymously. This does not simply mean do not put the call in a loop. The call could also be in a function called multiple times for example. However, remember that you could always make multiple instances of your anonymous structure using the array form of crx_new, instead of a loop, and because the call to crx_new is only made once this way, you suffer no extra resource loss.
However, unlike with classes, the need for anonymous structure, if used for data structures, should be far less common.

5.3.1 crx_registerStructure() / crxOop.crx_registerStructure()
crx_registerStructure("myNameSpace.myStructure",
{
//DEFINITION
});

5.4 Structure Components

5.4.1 Constructor
Look at Figure 02. The function CONSTRUCT takes an argument indicating the structure name and returns the construction function which is an equivilant to constructors in OOP languages. Please note the following
- The definition keyword CONSTRUCT is case sensitive even in the verbose syntax.
- A constructor may not throw an exception. This will cause a fatal error.
- A constructor may call the constructor of the inherited structure, one level up, but no more. This means, for example, that the constructor of a structure may not call the grand parent struture's constructor
- A default constructor is created when no constructor is defined. The default constructor is a function that takes no arguments.
- The structure constructor is called automatically with no arguments if the derived/child class constructor did not call it explicitly.
- A constructor may not return any thing.
To understand how to control construction order, you need to know the steps of construction. Look at the figure below. Given a structure 'D', which inherits class 'C', which itself inherits structures 'A' and 'B', as an example, construction of an instance of D follows the following steps:
- The instance is fully created.
- The constructor of the last inheriting structure is called first. In our example, this means the constructor of structre 'D'. This is the opposite of C++, for example, and intuition in general.
- After the constructor is called, CrxOop checks whether the constructor of the parent structure has been called. If not, the parent structre constructor is called. In our example, the parent constructor would be structure 'C'. If there is more than one immediate parent, CrxOop checks in the order defined in the structure definition (left to right), and calls the yet to be called constructors in that order.
- The process repeats until all constructors are called.
- The instance is now locked. This protects from new properties being created on the underlying javascript object.
Consider the following code and output:
crx_registerStructure("StructureA",
{
"VERBOSE": 1,
"public CONSTRUCT": function(pA)
{
console.log("CONSTRUCTING StructureA using pA = " + pA);
}
});
crx_registerStructure("StructureB",
{
"VERBOSE": 1,
"public CONSTRUCT": function(pA)
{
console.log("CONSTRUCTING StructureB using pA = " + pA);
}
});
crx_registerStructure("StructureC",
{
"VERBOSE": 1,
"inherits": ["StructureA", "StructureB"],
"public CONSTRUCT": function(pA)
{
console.log("CONSTRUCTING StructureC using pA = " + pA);
}
});
crx_registerStructure("StructureD",
{
"VERBOSE": 1,
"inherits": ["StructureC"],
"public CONSTRUCT": function(pA)
{
console.log("CONSTRUCTING StructureD using pA = " + pA);
}
});
crx_new("StructureD", 5);
CONSTRUCTING StructureC using pA = undefined
CONSTRUCTING StructureA using pA = undefined
CONSTRUCTING StructureB using pA = undefined
Let us follow what happened:
- The instance was fully created.
- The constructor of StructureD was called with the passed in parameter, 5.
- After the constructor of StructureD finished executing, CrxOop found that the constructor of StructureC was not called and called it without any parameters.
- After the constructor of StructureC finished executing, CrxOop found that the constructor of StructureA was not called and called it without any parameters.
- After the constructor of StructureA finished executing, Crxop went back to StructureC, and found that the constructor of StructureB was not called and called it without any parameters.
- The new instance is now locked.
If we wanted the constructor of StructureB execute its useful code before that of the constructor of StructureC, we call it as the first line of code in the constructor of StructureC. Changing the code of StructureC to the following:
crx_registerStructure("StructureA",
{
.
.
.
crx_registerStructure("StructureC",
{
"VERBOSE": 1,
"inherits": ["StructureA", "StructureB"],
"public CONSTRUCT": function(pA)
{
this.CONSTRUCT("StructureB")(pA);
console.log("CONSTRUCTING StructureC using pA = " + pA);
}
});
crx_new("ClassC", 5);
CONSTRUCTING StructureB using pA = undefined
CONSTRUCTING StructureC using pA = undefined
CONSTRUCTING StructureA using pA = undefined
Let us follow what happened:
- The instance was fully created.
- After the constructor of StructureD finished executing, CrxOop found that the constructor of StructureC was not called and called it without any parameters.
- The constructor of StructureC was called without any parameters, but it called the constructor of StructureB in its first line of code passing the parameter 'undefined', before its own useful code.
- The constructor of StructureC resumed executing its useful code.
- After the constructor of StructureC finished executing, CrxOop found that the constructor of StructureA was not called and called it without any parameters.
- The new instance is now locked.
One very important thing to notice is that the ancestors of StructureC at the StructureB branch fully finished executing their constructors before StructureC finished its own. This is important because it is sufficient in practice. If you are the developer of StructureC, and you want the second ancestor's constructor to be called first, all you care about is the second ancestor of StructureC, StructureB, and its ancestors to finish doing what they need in their constructors before your class begins executing its constructing code. The order of the construction of your ancestors at the StructureB branch would not matter. If it did, it would have been the worry of the developers of StructureB, and its ancesotrs if it had any.
If we want the parameter to be passed up to the constructor of StructureB, we would have to include an explicit constructor call in the constructor of StructureD.
crx_registerStructure("StructureA",
{
.
.
.
crx_registerStructure("StructureD",
{
"VERBOSE": 1,
"inherits": ["StructureC"],
"public CONSTRUCT": function(pA)
{
this.CONSTRUCT("StructureC")(pA);
console.log("CONSTRUCTING StructureD using pA = " + pA);
}
});
crx_new("ClassC", 5);
CONSTRUCTING StructureC using pA = 5
CONSTRUCTING StructureA using pA = undefined
CONSTRUCTING StructureD using pA = 5

5.4.4 Private Instance Variables and Functions
Private structure members are equivilant to private class members.
crx_registerStructure("ExampleStructureA",
{
PRIVATE:
{
VARS:
{
"privateVar": "I am ExampleStructureA::privateVar",
"privateVar2": "I am ExampleStructureA::privateVar2"
},
FUNCTIONS:
{
"privateFunction": function()
{
return "I am ExampleStructureA::privateFunction()";
}
}
},
SHARED:
{
PUBLIC:
{
FUNCTIONS:
{
"testFromExampleStructureA": function()
{
console.log(this.privateVar);
console.log(this.privateFunction());
console.log(this.privateVar2);
}
}
}
}
});
crx_registerStructure("ExampleStructureB",
{
INHERITS: ["ExampleStructureA"],
PRIVATE:
{
VARS:
{
"privateVar": "I am ExampleStructureB::privateVar",
},
FUNCTIONS:
{
"privateFunction": function()
{
return "I am ExampleStructureB::privateFunction()";
}
}
},
SHARED:
{
PUBLIC:
{
FUNCTIONS:
{
"testFromExampleStructureB": function()
{
console.log(this.privateVar);
console.log(this.privateFunction());
console.log(this.privateVar2);
}
}
}
}
});
var a = crx_new("ExampleStructureB");
console.log("From out side the instance functions");
console.log(a.privateVar);
console.log(a.privateFunction);
console.log("From inside ExampleStructureA function in an ExampleStructureB instance");
a.testFromExampleStructureA();
console.log("From inside ExampleStructureB function in an ExampleStructureB instance");
a.testFromExampleStructureB();
undefined
undefined
From inside ExampleStructureA function in an ExampleStructureB instance
I am ExampleStructureA::privateVar
I am ExampleStructureA::privateFunction()
I am ExampleStructureA::privateVar2
From inside ExampleStructureB function in an ExampleStructureB instance
I am ExampleStructureB::privateVar
I am ExampleStructureB::privateFunction()
undefined
crx_registerStructure("ExampleStructureA",
{
VERBOSE: 1,
"private var privateVar": "I am ExampleStructureA::privateVar",
"private var privateVar2": "I am ExampleStructureA::privateVar2",
"private function privateFunction": function()
{
return "I am ExampleStructureA::privateFunction()";
},
"shared public function testFromExampleStructureA": function()
{
console.log(this.privateVar);
console.log(this.privateFunction());
console.log(this.privateVar2);
}
});
crx_registerStructure("ExampleStructureB",
{
VERBOSE: 1,
INHERITS: ["ExampleStructureA"],
"private var privateVar": "I am ExampleStructureB::privateVar",
"private function privateFunction": function()
{
return "I am ExampleStructureB::privateFunction()";
},
"shared public function testFromExampleStructureB": function()
{
console.log(this.privateVar);
console.log(this.privateFunction());
console.log(this.privateVar2);
}
});
var a = crx_new("ExampleStructureB");
console.log("From out side the instance functions");
console.log(a.privateVar);
console.log(a.privateFunction);
console.log("From inside ExampleStructureA function in an ExampleStructureB instance");
a.testFromExampleStructureA();
console.log("From inside ExampleStructureB function in an ExampleStructureB instance");
a.testFromExampleStructureB();
undefined
undefined
From inside ExampleStructureA function in an ExampleStructureB instance
I am ExampleStructureA::privateVar
I am ExampleStructureA::privateFunction()
I am ExampleStructureA::privateVar2
From inside ExampleStructureB function in an ExampleStructureB instance
I am ExampleStructureB::privateVar
I am ExampleStructureB::privateFunction()
undefined
Private variables are the most expensive memory wise, but perhaps they are still cheaper overall than making use of var and closures the way traditionaly this is done. Further more, this privacy is not bound to the instance itself only, which is what you would expect from private access. A behavior that can not be mimicked using the traditional techniques only. Refer to 'O'.

5.5 Structure Keywords

5.5.1 this, THIS
'this' and 'THIS' are perhaps the most important of the structure keywords to understand. Internally, a structure instance consists of a shared public memory sector, a shared private memory sector, and a private memory sector. The 'this' keyword, which is the native Javascript 'this' keyword has access to all three sectors. 'THIS' on the other hand has access only to the shared public memory sector. This is why passing 'this' to functions as parameters, or from functions as returns, is dangerous and must never be done. Instead you would pass around 'THIS'.
crx_registerStructure("ExampleStructure",
{
SHARED:
{
PUBLIC:
{
VARS:
{
"sharedPublicVar": 1
},
FUNCTIONS:
{
"sharedPublicFunction": function(pExampleStructure)
{
// Remember 'THIS' is a structure keyword and hence is found on
// 'this' and can be accessed using either 'this':
console.log(this.THIS.sharedPublicVar);
// although for clarity you should never use 'THIS' to
// access instance members but instead use
console.log(this.sharedPublicVar);
// Or the object that is the structure instance:
console.log(pExampleStructure.THIS.sharedPublicVar);
// but again you should never use 'THIS' to access
// instance members but instead use
console.log(pExampleStructure.sharedPublicVar);
// 'THIS' is only meant for passing around. When wanting to
// return the instance from functions, instead of the
// dangerous code "return this", you would use
return this.THIS;
},
"sharedPublicFunction2": function()
{
// And when wanting to pass the instance to functions,
// instead of passing 'this', you would pass
// "this.THIS" like the following
var vReturn = this.sharedPublicFunction(this.THIS);
// Imagine the danger of the following if sharedPublicFunction
// above returned 'this' instead of 'THIS'
return vReturn;
}
}
}
}
});
crx_registerStructure("ExampleStructure",
{
"VERBOSE": 1,
"shared public var sharedPublicVar": 1,
"shared public function sharedPublicFunction": function(pExampleStructure)
{
// Remember 'THIS' is a structure keyword and hence is found on
// 'this' and can be accessed using either 'this':
console.log(this.THIS.sharedPublicVar);
// although for clarity you should never use 'THIS' to
// access instance members but instead use
console.log(this.sharedPublicVar);
// Or the object that is the structure instance:
console.log(pExampleStructure.THIS.sharedPublicVar);
// but again you should never use 'THIS' to access
// instance members but instead use
console.log(pExampleStructure.sharedPublicVar);
// 'THIS' is only meant for passing around. When wanting to
// return the instance from functions, instead of the
// dangerous code "return this", you would use
return this.THIS;
},
"shared public function sharedPublicFunction2": function()
{
// And when wanting to pass the instance to functions,
// instead of passing 'this', you would pass
// "this.THIS" like the following
var vReturn = this.sharedPublicFunction(this.THIS);
// Imagine the danger of the following if sharedPublicFunction
// above returned 'this' instead of 'THIS'
return vReturn;
}
});

5.5.2 O
'O' is a function. While 'this' allows you access to private variables and functions, as well as shared privates and shared publics, of the current instance, 'O' allows access to privates and shared privates of other instances. Internally 'O' will automatically return 'this', not 'THIS', of the object. Hence 'O' must never be passed to functions as parameters or from functions as returns, and because 'O' returns 'this', the rule applies to its return too. 'O' returns null if an error.
crx_registerStructure("ExampleStructure1",
{
SHARED:
{
PUBLIC:
{
VARS:
{
"sharedPublicVar": "I am the structure's sharedPublicVar"
},
FUNCTIONS:
{
"sharedPublicFunction": function(pExampleStructure1)
{
// Accessing private members
console.log(this.O(pExampleStructure1).privateVar);
// And shared private members
console.log(this.O(pExampleStructure1).sharedPrivateVar);
// And also shared publics,
console.log(this.O(pExampleStructure1).sharedPublicVar);
// but never do this, instead do,
console.log(pExampleStructure1.sharedPublicVar);
// Like 'this' in the previous section, 'O' must never be
// passed around, but this also applies to the return
// of 'O' which is another 'this'. For example, instead
// of returning this.O(pExampleStructure1) to return the
// instance pExampleStructure1, do the following
return this.O(pExampleStructure1).THIS;
}
}
},
PRIVATE:
{
VARS:
{
"sharedPrivateVar": "I am the structure's sharedPrivateVar"
}
}
},
PRIVATE:
{
VARS:
{
"privateVar": "ExampleStructure1::privateVar"
}
}
});
crx_registerStructure("ExampleStructure2",
{
INHERITS: ["ExampleStructure1"],
SHARED:
{
PUBLIC:
{
VARS:
{
"sharedPublicVar": "I am the structure's sharedPublicVar (2)"
},
FUNCTIONS:
{
"test": function(pExampleStructre2)
{
this.sharedPublicFunction(pExampleStructre2);
}
}
}
}
});
var instance = crx_new("ExampleStructure2");
instance.test(crx_new("ExampleStructure2"));
I am the structure's sharedPrivateVar
I am the structure's sharedPublicVar (2)
I am the structure's sharedPublicVar (2)
crx_registerStructure("ExampleStructure1",
{
"VERBOSE": 1,
"shared public var sharedPublicVar": "I am the structure's sharedPublicVar",
"shared public function sharedPublicFunction": function(pExampleStructure1)
{
// Accessing private members
console.log(this.O(pExampleStructure1).privateVar);
// And shared private members
console.log(this.O(pExampleStructure1).sharedPrivateVar);
// And also publics,
console.log(this.O(pExampleStructure1).sharedPublicVar);
// but never do this, instead do,
console.log(pExampleStructure1.sharedPublicVar);
// Like 'this' in the previous section, 'O' must never be
// passed around, but this also applies to the return
// of 'O' which is another 'this'. For example, instead
// of returning this.O(pExampleStructure1) to return the
// instance pExampleStructure1, do the following
return this.O(pExampleStructure1).THIS;
},
"shared private var sharedPrivateVar": "I am the structure's sharedPrivateVar",
"private var privateVar": "ExampleStructure1::privateVar"
});
crx_registerStructure("ExampleStructure2",
{
"VERBOSE": 1,
"inherits": ["ExampleStructure1"],
"shared public var sharedPublicVar": "I am the structure's sharedPublicVar (2)",
"shared public function test": function(pExampleStructre2)
{
this.sharedPublicFunction(pExampleStructre2);
}
});
var instance = crx_new("ExampleStructure2");
instance.test(crx_new("ExampleStructure2"));
I am the structure's sharedPrivateVar
I am the structure's sharedPublicVar (2)
I am the structure's sharedPublicVar (2)

5.5.3 CONSTRUCT
'CONSTRUCT' is a function. The structure keyword 'CONSTRUCT' is not to be confused with the definition keyword 'CONSTRUCT'. 'CONSTRUCT' returns the constructor function defined in the class definition, and is only available during construction. The only valid use of CONSTRUCT is to call a parent constructor using "this.CONSTRUCT('nameOfInheritedStructure')()" when an explicit call is needed. Never pass "CONSTRUCT" to functions as parameters, or from functions as returns.
crx_registerStructure("ExampleStructure1",
{
PUBLIC:
{
CONSTRUCT: function(pValue)
{
if(pValue !== undefined)
{this.privateVar = pValue;}
},
},
SHARED:
{
PUBLIC:
{
FUNCTIONS:
{
'test': function()
{
console.log(this.privateVar);
}
}
}
},
PRIVATE:
{
VARS:
{
"privateVar": "I am default privateVar"
}
}
});
crx_registerStructure("ExampleStructure2",
{
PUBLIC:
{
CONSTRUCT: function(pValue)
{}
}
});
crx_registerStructure("ExampleStructure3",
{
INHERITS: ["ExampleStructure1"],
PUBLIC:
{
CONSTRUCT: function(pValue)
{
// Calling a parent structure's constructor explicitly to
// pass the necessary parameters
this.CONSTRUCT("ExampleStructure1")(pValue);
}
}
});
crx_registerStructure("ExampleClass4",
{
INHERITS: ["ExampleStructure2", "ExampleStructure3"],
PUBLIC:
{
CONSTRUCT: function(pValue)
{
// Calling a parent structure's constructor explicitly to
// pass the necessary parameters
this.CONSTRUCT("ExampleStructure3")(pValue);
}
}
});
var instance = crx_new("ExampleClass4", "I am a new privateVar");
instance.test();
crx_registerStructure("ExampleStructure1",
{
"VERBOSE": 1,
"public CONSTRUCT": function(pValue)
{
if(pValue !== undefined)
{this.privateVar = pValue;}
},
"shared public function test": function()
{
console.log(this.privateVar);
},
"private var privateVar": "I am default privateVar"
});
crx_registerStructure("ExampleStructure2",
{
"VERBOSE": 1,
"public CONSTRUCT": function(pValue)
{}
});
crx_registerStructure("ExampleStructure3",
{
"VERBOSE": 1,
"inherits": ["ExampleStructure1"],
"public CONSTRUCT": function(pValue)
{
// Calling the parent class's constructor explicitly to
// pass the necessary parameters
this.CONSTRUCT("ExampleStructure1")(pValue);
}
});
crx_registerStructure("ExampleStructure4",
{
"VERBOSE": 1,
"inherits": ["ExampleStructure2", "ExampleStructure3"],
"public CONSTRUCT": function(pValue)
{
// Calling a parent structure's constructor explicitly to
// pass the necessary parameters
this.CONSTRUCT("ExampleStructure3")(pValue);
}
});
var instance = crx_new("ExampleStructure4", "I am a new privateVar");
instance.test();

5.5.4 SR
"SR" is roughly the equivilant of "::" in C++. However, it can only be used on instances. Its useful to accessing functions that are defined as shared public, or shared private, but were overriden by inheriting structures. The use of "SR" should generally be kept to a minimum.
"SR" can not be used to access variables. This is unlike SR for classes. "SR" does not allow the use of null for the structure name, also unlike SR for classes.
"SR", like "O" for example, must never be passed to functions as parameters, or returned from functions as returns. "SR" will cause fatal errors if the structure is not in the instance's structure inheritance map at all, or if misused.
crx_registerStructure("ExampleStructure1",
{
SHARED:
{
PUBLIC:
{
FUNCTIONS:
{
sharedPulicFunction: function(pA)
{
return ("ExampleStructure1::sharedPulicFunction with parameter " + pA);
},
testFromExampleStructure1: function(pA)
{
// Calling ExampleStructure1::sharedPulicFunction or
// ExampleStructure1::sharedPrivateFunction
// will not work if an inheriting structure in the instance
// overrides the functions, like ExampleStructure3.
this.sharedPulicFunction(5);
this.sharedPrivateFunction(6);
// Instead we have to use 'SR'
this.SR("ExampleStructure1", "sharedPulicFunction", 5);
this.SR("ExampleStructure1", "sharedPrivateFunction", 6);
// The following would call
// ExampleStructure2::sharedPulicFunction
this.SR("ExampleStructure2", "sharedPulicFunction", 7);
// Printing the result of the above
console.log("TESTING FROM ExampleStructure1");
console.log(this.sharedPulicFunction(5));
console.log(this.sharedPrivateFunction(6));
console.log(this.SR("ExampleStructure1", "sharedPulicFunction", 5));
console.log(this.SR("ExampleStructure1", "sharedPrivateFunction", 6));
console.log(this.SR("ExampleStructure2", "sharedPulicFunction", 7));
}
}
},
PRIVATE:
{
FUNCTIONS:
{
sharedPrivateFunction: function(pA)
{
return ("ExampleStructure1::sharedPrivateFunction with parameter " + pA);
}
}
}
}
});
crx_registerStructure("ExampleStructure2",
{
SHARED:
{
PUBLIC:
{
FUNCTIONS:
{
sharedPulicFunction: function(pA)
{
return ("ExampleStructure2::sharedPulicFunction with parameter " + pA);
}
}
},
PRIVATE:
{
FUNCTIONS:
{
sharedPrivateFunction: function(pA)
{
return ("ExampleStructure2::sharedPrivateFunction with parameter " + pA);
}
}
}
}
});
crx_registerStructure("ExampleStructure3",
{
INHERITS: ["ExampleStructure1", "ExampleStructure2"],
SHARED:
{
PUBLIC:
{
FUNCTIONS:
{
sharedPulicFunction: function(pA)
{
return ("ExampleStructure3::sharedPulicFunction with parameter " + pA);
},
testFromExampleStructure3: function(pA)
{
this.sharedPulicFunction(8);
this.sharedPrivateFunction(9);
this.SR("ExampleStructure1", "sharedPulicFunction", 8);
this.SR("ExampleStructure1", "sharedPrivateFunction", 9);
this.SR("ExampleStructure2", "sharedPulicFunction", 8);
this.SR("ExampleStructure2", "sharedPrivateFunction", 9);
// Printing the result of the above
console.log("TESTING FROM ExampleStructure3");
console.log(this.sharedPulicFunction(8));
console.log(this.sharedPrivateFunction(9));
console.log(this.SR("ExampleStructure1", "sharedPulicFunction", 8));
console.log(this.SR("ExampleStructure1", "sharedPrivateFunction", 9));
console.log(this.SR("ExampleStructure2", "sharedPulicFunction", 8));
console.log(this.SR("ExampleStructure2", "sharedPrivateFunction", 9));
}
}
},
PRIVATE:
{
FUNCTIONS:
{
sharedPrivateFunction: function(pA)
{
return ("ExampleStructure3::sharedPrivateFunction with parameter " + pA);
}
}
}
}
});
var instance = crx_new("ExampleStructure3");
instance.testFromExampleStructure1();
instance.testFromExampleStructure3();
//SR is also available on the instance
console.log("------------------");
console.log(instance.SR("ExampleStructure2", "sharedPulicFunction", 10));
ExampleStructure3::sharedPulicFunction with parameter 5
ExampleStructure3::sharedPrivateFunction with parameter 6
ExampleStructure1::sharedPulicFunction with parameter 5
ExampleStructure1::sharedPrivateFunction with parameter 6
ExampleStructure2::sharedPulicFunction with parameter 7
TESTING FROM ExampleStructure3
ExampleStructure3::sharedPulicFunction with parameter 8
ExampleStructure3::sharedPrivateFunction with parameter 9
ExampleStructure1::sharedPulicFunction with parameter 8
ExampleStructure1::sharedPrivateFunction with parameter 9
ExampleStructure2::sharedPulicFunction with parameter 8
ExampleStructure2::sharedPrivateFunction with parameter 9
------------------
ExampleStructure2::sharedPulicFunction with parameter 10
crx_registerStructure("ExampleStructure1",
{
VERBOSE: 1,
"shared public function sharedPulicFunction": function(pA)
{
return ("ExampleStructure1::sharedPulicFunction with parameter " + pA);
},
"shared public function testFromExampleStructure1": function(pA)
{
// Calling ExampleStructure1::sharedPulicFunction or
// ExampleStructure1::sharedPrivateFunction
// will not work if an inheriting structure in the instance
// overrides the functions, like ExampleStructure3.
this.sharedPulicFunction(5);
this.sharedPrivateFunction(6);
// Instead we have to use 'SR'
this.SR("ExampleStructure1", "sharedPulicFunction", 5);
this.SR("ExampleStructure1", "sharedPrivateFunction", 6);
// The following would call
// ExampleStructure2::sharedPulicFunction
this.SR("ExampleStructure2", "sharedPulicFunction", 7);
// Printing the result of the above
console.log("TESTING FROM ExampleStructure1");
console.log(this.sharedPulicFunction(5));
console.log(this.sharedPrivateFunction(6));
console.log(this.SR("ExampleStructure1", "sharedPulicFunction", 5));
console.log(this.SR("ExampleStructure1", "sharedPrivateFunction", 6));
console.log(this.SR("ExampleStructure2", "sharedPulicFunction", 7));
},
"shared private function sharedPrivateFunction": function(pA)
{
return ("ExampleStructure1::sharedPrivateFunction with parameter " + pA);
}
});
crx_registerStructure("ExampleStructure2",
{
VERBOSE: 1,
"shared public function sharedPulicFunction": function(pA)
{
return ("ExampleStructure2::sharedPulicFunction with parameter " + pA);
},
"shared private function sharedPrivateFunction": function(pA)
{
return ("ExampleStructure2::sharedPrivateFunction with parameter " + pA);
}
});
crx_registerStructure("ExampleStructure3",
{
VERBOSE: 1,
"inherits": ["ExampleStructure1", "ExampleStructure2"],
"shared public function sharedPulicFunction": function(pA)
{
return ("ExampleStructure3::sharedPulicFunction with parameter " + pA);
},
"shared public function testFromExampleStructure3": function(pA)
{
this.sharedPulicFunction(8);
this.sharedPrivateFunction(9);
this.SR("ExampleStructure1", "sharedPulicFunction", 8);
this.SR("ExampleStructure1", "sharedPrivateFunction", 9);
this.SR("ExampleStructure2", "sharedPulicFunction", 8);
this.SR("ExampleStructure2", "sharedPrivateFunction", 9);
// Printing the result of the above
console.log("TESTING FROM ExampleStructure3");
console.log(this.sharedPulicFunction(8));
console.log(this.sharedPrivateFunction(9));
console.log(this.SR("ExampleStructure1", "sharedPulicFunction", 8));
console.log(this.SR("ExampleStructure1", "sharedPrivateFunction", 9));
console.log(this.SR("ExampleStructure2", "sharedPulicFunction", 8));
console.log(this.SR("ExampleStructure2", "sharedPrivateFunction", 9));
},
"shared private function sharedPrivateFunction": function(pA)
{
return ("ExampleStructure3::sharedPrivateFunction with parameter " + pA);
}
});
var instance = crx_new("ExampleStructure3");
instance.testFromExampleStructure1();
instance.testFromExampleStructure3();
//SR is also available on the instance
console.log("------------------");
console.log(instance.SR("ExampleStructure2", "sharedPulicFunction", 10));
ExampleStructure3::sharedPulicFunction with parameter 5
ExampleStructure3::sharedPrivateFunction with parameter 6
ExampleStructure1::sharedPulicFunction with parameter 5
ExampleStructure1::sharedPrivateFunction with parameter 6
ExampleStructure2::sharedPulicFunction with parameter 7
TESTING FROM ExampleStructure3
ExampleStructure3::sharedPulicFunction with parameter 8
ExampleStructure3::sharedPrivateFunction with parameter 9
ExampleStructure1::sharedPulicFunction with parameter 8
ExampleStructure1::sharedPrivateFunction with parameter 9
ExampleStructure2::sharedPulicFunction with parameter 8
ExampleStructure2::sharedPrivateFunction with parameter 9
------------------
ExampleStructure2::sharedPulicFunction with parameter 10

5.5.5 HASOWN
"HASOWN" is the equivilant of the built in hasOwnProperty function and is meant to be used for iteration on the structure members.
crxOop.HASOWN_SCOPE_SHARED_PUBLIC = 8 for members that are "shared public"
crxOop.HASOWN_SCOPE_SHARED_PRIVATE = 16 for members that are "shared private"
crxOop.HASOWN_SCOPE_PRIVATE = 32 for members that are "private"
Defaults to 8 (crxOop.HASOWN_SCOPE_SHARED_PUBLIC)
crxOop.HASOWN_TYPE_VAR = 1 for members that are variables
crxOop.HASOWN_TYPE_FUNCTION = 2 for members that are functions
crxOop.HASOWN_TYPE_FOREIGN = 4 for members that were not defined in the structure definition
Defaults to 1 (crxOop.HASOWN_TYPE_VAR)
Foreign members are members declared in the constructor, not in the class definition. Such declaration is highly discouraged, but allowed.
crx_registerStructure("MyStructure1",
{
"VERBOSE": 1,
"shared public var sharedPublicVar": "MyStructure1::sharedPublicVar",
"shared public function sharedPublicFunction": function()
{
return "MyStructure1::sharedPublicFunction()";
},
"shared private var sharedPrivateVar": "MyStructure1::sharedPrivateVar",
"shared private function sharedPrivateFunction": function()
{
return "MyStructure1::sharedPrivateFunction()";
},
"private var privateVar": "MyStructure1::privateVar",
"shared public function test2": function()
{
console.log("---");
console.log("---");
console.log("Iterating over all variables and functions of current structure (MyStructure1)");
console.log("---");
for(tKey in this)
{
if(this.HASOWN(tKey, crxOop.HASOWN_SCOPE_SHARED_PUBLIC | crxOop.HASOWN_SCOPE_SHARED_PRIVATE |
crxOop.HASOWN_SCOPE_PRIVATE, crxOop.HASOWN_TYPE_VAR | crxOop.HASOWN_TYPE_FUNCTION, true))
{
console.log(tKey);
}
}
}
});
crx_registerStructure("MyStructure2",
{
"VERBOSE": 1,
"inherits": ["MyStructure1"],
"shared public var sharedPublicVar2": "MyStructure2::sharedPublicVar2",
"shared public function sharedPublicFunction": function()
{
return "MyStructure2::sharedPublicFunction()";
},
"shared private var sharedPrivateVar": "MyStructure2::sharedPrivateVar",
"shared public function test1": function()
{
console.log("Iterating over shared public and shared private variables and functions");
console.log("---");
for(tKey in this)
{
if(this.HASOWN(tKey, crxOop.HASOWN_SCOPE_SHARED_PUBLIC | crxOop.HASOWN_SCOPE_SHARED_PRIVATE,
crxOop.HASOWN_TYPE_VAR | crxOop.HASOWN_TYPE_FUNCTION))
{
console.log(tKey);
}
}
console.log("---");
console.log("---");
console.log("Iterating over shared private and private variables");
console.log("---");
for(tKey in this)
{
if(this.HASOWN(tKey, crxOop.HASOWN_SCOPE_SHARED_PRIVATE | crxOop.HASOWN_SCOPE_PRIVATE,
crxOop.HASOWN_TYPE_VAR))
{
console.log(tKey);
}
}
console.log("---");
console.log("---");
console.log("Iterating over all variables and functions of current structure (MyStructure2)");
console.log("---");
for(tKey in this)
{
if(this.HASOWN(tKey, crxOop.HASOWN_SCOPE_SHARED_PUBLIC | crxOop.HASOWN_SCOPE_SHARED_PRIVATE |
crxOop.HASOWN_SCOPE_PRIVATE, crxOop.HASOWN_TYPE_VAR | crxOop.HASOWN_TYPE_FUNCTION, true))
{
console.log(tKey);
}
}
}
});
var gMyStructure = crx_new("MyStructure2");
gMyStructure.test1();
gMyStructure.test2();
---
sharedPrivateFunction
sharedPublicVar
sharedPublicVar2
sharedPrivateVar
sharedPublicFunction
test2
test1
---
---
Iterating over shared private and private variables
---
sharedPrivateVar
---
---
Iterating over all variables and functions of current structure (MyStructure2)
---
sharedPrivateFunction
sharedPublicVar
sharedPublicVar2
sharedPrivateVar
sharedPublicFunction
test2
test1
---
---
Iterating over all variables and functions of current structure (MyStructure1)
---
privateVar
sharedPrivateFunction
sharedPublicVar
sharedPrivateVar
sharedPublicFunction
test2

6.0 Data Typing
CrxOop comes with its own typing system in addition to what javascript provides. Two important functions are provided to complement what javascript provides, crxOop.instanceOf and crxOop.typeOf which complement the native 'instanceof' and 'typeof'. Beyond these two, there are also crxOop.isClassExtending, crxOop.isClassChaining, crxOop.isClassImplementing, crxOop.isStructureInheriting, and crxOop.isStructureContaining.

6.1 crxOop.typeOf
crxOop.typeOf complements the native 'typeof', but CrxOop only understands four types,
- $CRX_DEFINITION__INTERFACE: An interface definition object.
- $CRX_DEFINITION__CLASS: A class definition object.
- $CRX_DEFINITION__STRUCTURE: A structure definition object.
- $CRX_OBJECT: An instance of a class or a structure.
- $CRX__native: Every thing else. A native type.
var ExampleInterfaceDefinition =
{
"function1": 0
};
crx_registerInterface("ExampleInterface", ExampleInterfaceDefinition);
var ExampleClassDefinition =
{
PUBLIC:
{
VARS:
{
"publicVar": "I am ExampleClass"
}
}
};
crx_registerClass("ExampleClass", ExampleClassDefinition);
var ExampleStructureDefinition =
{
SHARED:
{
PUBLIC:
{
VARS:
{
"sharedPublicVar": "I am ExampleStructure"
}
}
}
}
crx_registerStructure("ExampleStructure", ExampleStructureDefinition);
console.log(crxOop.typeOf(ExampleInterfaceDefinition));
console.log(crxOop.typeOf("ExampleInterface"));
console.log(crxOop.typeOf(ExampleClassDefinition));
console.log(crxOop.typeOf("ExampleClass"));
console.log(crxOop.typeOf(ExampleStructureDefinition));
console.log(crxOop.typeOf("ExampleStructure"));
console.log(crxOop.typeOf(crx_new("ExampleClass")));
console.log(crxOop.typeOf(crx_new("ExampleStructure")));
console.log(crxOop.typeOf("a string"));
console.log(crxOop.typeOf(5));
$CRX__native
$CRX_DEFINITION__CLASS
$CRX__native
$CRX_DEFINITION__STRUCTURE
$CRX__native
$CRX_OBJECT
$CRX_OBJECT
$CRX__native
$CRX__native
var ExampleInterfaceDefinition =
{
"function1": 0
};
crx_registerInterface("ExampleInterface", ExampleInterfaceDefinition);
var ExampleClassDefinition =
{
"VERBOSE": 1,
"public var publicVar": "I am ExampleClass"
};
crx_registerClass("ExampleClass", ExampleClassDefinition);
var ExampleStructureDefinition =
{
"VERBOSE": 1,
"shared public var sharedPublicVar": "I am ExampleStructure"
}
crx_registerStructure("ExampleStructure", ExampleStructureDefinition);
console.log(crxOop.typeOf(ExampleInterfaceDefinition));
console.log(crxOop.typeOf("ExampleInterface"));
console.log(crxOop.typeOf(ExampleClassDefinition));
console.log(crxOop.typeOf("ExampleClass"));
console.log(crxOop.typeOf(ExampleStructureDefinition));
console.log(crxOop.typeOf("ExampleStructure"));
console.log(crxOop.typeOf(crx_new("ExampleClass")));
console.log(crxOop.typeOf(crx_new("ExampleStructure")));
console.log(crxOop.typeOf("a string"));
console.log(crxOop.typeOf(5));
$CRX__native
$CRX_DEFINITION__CLASS
$CRX__native
$CRX_DEFINITION__STRUCTURE
$CRX__native
$CRX_OBJECT
$CRX_OBJECT
$CRX__native
$CRX__native
Notice how registered names of classes and interface are still of type $CRX__native.

6.2 crxOop.instanceOf
crxOop.instanceOf complements the native 'instanceof'. CrxOop, as mentioned earlier, implements class public inheritance. An instance of ClassC that extends ClassB is also an instance of ClassB, and if ClassB extends ClassA, that instance of ClassC is also an instance of ClassA, and so forth. If ClassA implemented InterfaceA, then the instance of ClassC would also be in 'instance' of InterfaceA, however non castable to InterfaceA.
crx_registerInterface("ExampleInterface1",
{
});
crx_registerClass("ExampleClass1",
{
IMPLEMENTS: ["ExampleInterface1"]
});
crx_registerClass("ExampleClass2",
{
EXTENDS: "ExampleClass1"
});
crx_registerInterface("ExampleInterface3",
{
});
crx_registerClass("ExampleClass3",
{
IMPLEMENTS: ["ExampleInterface3"],
EXTENDS: "ExampleClass2"
});
vInstance1 = crx_new("ExampleClass1");
vInstance2 = crx_new("ExampleClass2");
vInstance3 = crx_new("ExampleClass3");
console.log((crxOop.instanceOf(vInstance1, "ExampleClass2") ? "true" : "false"));
console.log((crxOop.instanceOf(vInstance2, "ExampleClass1") ? "true" : "false"));
console.log((crxOop.instanceOf(vInstance3, "ExampleClass1") ? "true" : "false"));
console.log((crxOop.instanceOf(vInstance3, "ExampleClass2") ? "true" : "false"));
console.log((crxOop.instanceOf(vInstance3, "ExampleClass3") ? "true" : "false"));
console.log((crxOop.instanceOf(vInstance3.CAST("ExampleClass1"), "ExampleClass3") ? "true" : "false"));
console.log("-----------------------------------");
console.log((crxOop.instanceOf(vInstance1, "ExampleInterface1") ? "true" : "false"));
console.log((crxOop.instanceOf(vInstance2, "ExampleInterface1") ? "true" : "false"));
console.log((crxOop.instanceOf(vInstance3, "ExampleInterface1") ? "true" : "false"));
console.log((crxOop.instanceOf(vInstance3, "ExampleInterface3") ? "true" : "false"));
console.log((crxOop.instanceOf(vInstance3.CAST("ExampleClass1"), "ExampleInterface3") ? "true" : "false"));
true
true
true
true
false
-----------------------------------
true
true
true
true
false
crx_registerInterface("ExampleInterface1",
{
});
crx_registerClass("ExampleClass1",
{
"VERBOSE": 1,
"implements": ["ExampleInterface1"]
});
crx_registerClass("ExampleClass2",
{
"VERBOSE": 1,
"extends": "ExampleClass1"
});
crx_registerInterface("ExampleInterface3",
{
});
crx_registerClass("ExampleClass3",
{
"VERBOSE": 1,
"implements": ["ExampleInterface3"],
"extends": "ExampleClass2"
});
vInstance1 = crx_new("ExampleClass1");
vInstance2 = crx_new("ExampleClass2");
vInstance3 = crx_new("ExampleClass3");
console.log((crxOop.instanceOf(vInstance1, "ExampleClass2") ? "true" : "false"));
console.log((crxOop.instanceOf(vInstance2, "ExampleClass1") ? "true" : "false"));
console.log((crxOop.instanceOf(vInstance3, "ExampleClass1") ? "true" : "false"));
console.log((crxOop.instanceOf(vInstance3, "ExampleClass2") ? "true" : "false"));
console.log((crxOop.instanceOf(vInstance3, "ExampleClass3") ? "true" : "false"));
console.log((crxOop.instanceOf(vInstance3.CAST("ExampleClass1"), "ExampleClass3") ? "true" : "false"));
console.log("-----------------------------------");
console.log((crxOop.instanceOf(vInstance1, "ExampleInterface1") ? "true" : "false"));
console.log((crxOop.instanceOf(vInstance2, "ExampleInterface1") ? "true" : "false"));
console.log((crxOop.instanceOf(vInstance3, "ExampleInterface1") ? "true" : "false"));
console.log((crxOop.instanceOf(vInstance3, "ExampleInterface3") ? "true" : "false"));
console.log((crxOop.instanceOf(vInstance3.CAST("ExampleClass1"), "ExampleInterface3") ? "true" : "false"));
true
true
true
true
false
-----------------------------------
true
true
true
true
false
Notice how when vInstance3, an instance of ExampleClass3, was casted to ExampleClass1, it was no longer an instance of ExampleClass3 nor the interface that ExampleClass3 implements.
crxOop.instanceOf can also be used as plain instanceof, however this only works when both parameters are of type $CRX__native, see crxOop.typeOf, and the second parameter is not a string.

7.0 Class And Structure Typing
CrxOop provides minimal support for class and structure reflection which we think should be sufficient for most, if not all, cases, and where it is not sufficient, an alternative better solution to reflection should exist. Seven functions are provided, crxOop.isClassExtending, crxOop.isClassChaining, crxOop.isClassImplementing, crxOop.isClassRegistered, crxOop.isStructureInheriting, crxOop.isStructureContaining, and crxOop.isStructureRegistered. These functions are not meant to reflect on the instances themselves, but instead on class definitions. The idea is to allow a foreign class library for example to work with your own.

7.1 crxOop.isClassExtending
crxOop.isClassExtending can be used to inspect during run time if a class, not an instance, extends another class. If any of the parameters is not a valid class definition object, or registered class name, crxOop.isClassExtending will issue a fatal error.
crx_registerClass("ExampleClass1",
{
});
crx_registerClass("ExampleClass2",
{
EXTENDS: "ExampleClass1"
});
crx_registerClass("ExampleClass3",
{
EXTENDS: "ExampleClass2"
});
console.log((crxOop.isClassExtending("ExampleClass1", "ExampleClass2") ? "true" : "false"));
console.log((crxOop.isClassExtending("ExampleClass2", "ExampleClass1") ? "true" : "false"));
console.log((crxOop.isClassExtending("ExampleClass3", "ExampleClass2") ? "true" : "false"));
console.log((crxOop.isClassExtending("ExampleClass3", "ExampleClass3") ? "true" : "false"));
console.log((crxOop.isClassExtending("ExampleClass1", "ExampleClass3") ? "true" : "false"));
true
true
false
false
crx_registerClass("ExampleClass1",
{
"VERBOSE": 1
});
crx_registerClass("ExampleClass2",
{
"VERBOSE": 1,
"extends": "ExampleClass1"
});
crx_registerClass("ExampleClass3",
{
"VERBOSE": 1,
"extends": "ExampleClass2"
});
console.log((crxOop.isClassExtending("ExampleClass1", "ExampleClass2") ? "true" : "false"));
console.log((crxOop.isClassExtending("ExampleClass2", "ExampleClass1") ? "true" : "false"));
console.log((crxOop.isClassExtending("ExampleClass3", "ExampleClass2") ? "true" : "false"));
console.log((crxOop.isClassExtending("ExampleClass3", "ExampleClass3") ? "true" : "false"));
console.log((crxOop.isClassExtending("ExampleClass1", "ExampleClass3") ? "true" : "false"));
true
true
false
false
Notice how a class is not extending itself. If you also want this case to return true, check the next section on crxOop.isClassChaining().

7.2 crxOop.isClassChaining
crxOop.isClassChaining can be used during run time to inspect if pure instances of a class can be casted to another class. The function behaves exactly like crxOop.isClassExtending, but will return true if a class is checked against itself. If any of the parameters is not a valid class definition object, or registered class name, crxOop.isClassChaining will issue a fatal error.
crx_registerClass("ExampleClass1",
{
});
crx_registerClass("ExampleClass2",
{
EXTENDS: "ExampleClass1"
});
crx_registerClass("ExampleClass3",
{
EXTENDS: "ExampleClass2"
});
console.log((crxOop.isClassChaining("ExampleClass1", "ExampleClass2") ? "true" : "false"));
console.log((crxOop.isClassChaining("ExampleClass2", "ExampleClass1") ? "true" : "false"));
console.log((crxOop.isClassChaining("ExampleClass3", "ExampleClass2") ? "true" : "false"));
console.log((crxOop.isClassChaining("ExampleClass3", "ExampleClass3") ? "true" : "false"));
console.log((crxOop.isClassChaining("ExampleClass1", "ExampleClass3") ? "true" : "false"));
true
true
true
false
crx_registerClass("ExampleClass1",
{
"VERBOSE": 1
});
crx_registerClass("ExampleClass2",
{
"VERBOSE": 1,
"extends": "ExampleClass1"
});
crx_registerClass("ExampleClass3",
{
"VERBOSE": 1,
"extends": "ExampleClass2"
});
console.log((crxOop.isClassChaining("ExampleClass1", "ExampleClass2") ? "true" : "false"));
console.log((crxOop.isClassChaining("ExampleClass2", "ExampleClass1") ? "true" : "false"));
console.log((crxOop.isClassChaining("ExampleClass3", "ExampleClass2") ? "true" : "false"));
console.log((crxOop.isClassChaining("ExampleClass3", "ExampleClass3") ? "true" : "false"));
console.log((crxOop.isClassChaining("ExampleClass1", "ExampleClass3") ? "true" : "false"));
true
true
true
false

7.3 crxOop.isClassImplementing
crxOop.isClassImplementing() can be used to check during run time whether a class implements a particular interface or not. If the first parameter is not a valid class definition object, or registered class name, or the second parameter is not a valid interface definition object, or registered interface name, crxOop.isClassImplementing will issue a fatal error.
crx_registerInterface("ExampleInterface1b",
{
});
crx_registerInterface("ExampleInterface1",
{
INHERITS: ["ExampleInterface1b"]
});
crx_registerClass("ExampleClass1",
{
IMPLEMENTS: ["ExampleInterface1"]
});
crx_registerClass("ExampleClass2",
{
EXTENDS: "ExampleClass1"
});
crx_registerInterface("ExampleInterface3",
{
});
crx_registerClass("ExampleClass3",
{
IMPLEMENTS: ["ExampleInterface3"],
EXTENDS: "ExampleClass2"
});
console.log((crxOop.isClassImplementing("ExampleClass1", "ExampleInterface1") ? "true" : "false"));
console.log((crxOop.isClassImplementing("ExampleClass1", "ExampleInterface1b") ? "true" : "false"));
console.log((crxOop.isClassImplementing("ExampleClass1", "ExampleInterface3") ? "true" : "false"));
console.log((crxOop.isClassImplementing("ExampleClass2", "ExampleInterface1") ? "true" : "false"));
console.log((crxOop.isClassImplementing("ExampleClass3", "ExampleInterface3") ? "true" : "false"));
console.log((crxOop.isClassImplementing("ExampleClass3", "ExampleInterface1") ? "true" : "false"));
console.log((crxOop.isClassImplementing("ExampleClass3", "ExampleInterface1b") ? "true" : "false"));
true
false
true
true
true
true
crx_registerInterface("ExampleInterface1b",
{
});
crx_registerInterface("ExampleInterface1",
{
INHERITS: ["ExampleInterface1b"]
});
crx_registerClass("ExampleClass1",
{
"VERBOSE": 1,
"implements": ["ExampleInterface1"]
});
crx_registerClass("ExampleClass2",
{
"VERBOSE": 1,
"extends": "ExampleClass1"
});
crx_registerInterface("ExampleInterface3",
{
});
crx_registerClass("ExampleClass3",
{
"VERBOSE": 1,
"implements": ["ExampleInterface3"],
"extends": "ExampleClass2"
});
console.log((crxOop.isClassImplementing("ExampleClass1", "ExampleInterface1") ? "true" : "false"));
console.log((crxOop.isClassImplementing("ExampleClass1", "ExampleInterface1b") ? "true" : "false"));
console.log((crxOop.isClassImplementing("ExampleClass1", "ExampleInterface3") ? "true" : "false"));
console.log((crxOop.isClassImplementing("ExampleClass2", "ExampleInterface1") ? "true" : "false"));
console.log((crxOop.isClassImplementing("ExampleClass3", "ExampleInterface3") ? "true" : "false"));
console.log((crxOop.isClassImplementing("ExampleClass3", "ExampleInterface1") ? "true" : "false"));
console.log((crxOop.isClassImplementing("ExampleClass3", "ExampleInterface1b") ? "true" : "false"));
true
false
true
true
true
true
Notice how crxOop.isClassImplementing continues to return true if not the class itself, but a class in its extension chain implements the interface.

7.4 crxOop.isClassRegistered
crxOop.isClassRegistered can be used to check during run time whether a class has been registered or not. The first parameter can be either a class definition object (object), or a class name (string).
crx_registerClass("ExampleClass1",
{
});
crx_registerClass("ExampleClass2",
{
});
console.log((crxOop.isClassRegistered("ExampleClass1") ? "true" : "false"));
console.log((crxOop.isClassRegistered("ExampleClass2") ? "true" : "false"));
console.log((crxOop.isClassRegistered("ExampleClass3") ? "true" : "false"));
true
false
crx_registerClass("ExampleClass1",
{
"VERBOSE": 1
});
crx_registerClass("ExampleClass2",
{
"VERBOSE": 1
});
console.log((crxOop.isClassRegistered("ExampleClass1") ? "true" : "false"));
console.log((crxOop.isClassRegistered("ExampleClass2") ? "true" : "false"));
console.log((crxOop.isClassRegistered("ExampleClass3") ? "true" : "false"));
true
false

7.5 crxOop.isStructureInheriting
crxOop.isStructureInheriting can be used to inspect during run time if a structure, not an instance, inherits another structure. If any of the parameters is not a valid structure definition object, or registered structure name, crxOop.isStructureInheriting will issue a fatal error.
crx_registerStructure("ExampleStructure1",
{
});
crx_registerStructure("ExampleStructure2",
{
INHERITS: ["ExampleStructure1"]
});
crx_registerStructure("ExampleStructure3",
{
INHERITS: ["ExampleStructure2"]
});
console.log((crxOop.isStructureInheriting("ExampleStructure1", "ExampleStructure2") ? "true" : "false"));
console.log((crxOop.isStructureInheriting("ExampleStructure2", "ExampleStructure1") ? "true" : "false"));
console.log((crxOop.isStructureInheriting("ExampleStructure3", "ExampleStructure2") ? "true" : "false"));
console.log((crxOop.isStructureInheriting("ExampleStructure3", "ExampleStructure3") ? "true" : "false"));
console.log((crxOop.isStructureInheriting("ExampleStructure1", "ExampleStructure3") ? "true" : "false"));
true
true
false
false
crx_registerStructure("ExampleStructure1",
{
"VERBOSE": 1
});
crx_registerStructure("ExampleStructure2",
{
"VERBOSE": 1,
INHERITS: ["ExampleStructure1"]
});
crx_registerStructure("ExampleStructure3",
{
"VERBOSE": 1,
INHERITS: ["ExampleStructure2"]
});
console.log((crxOop.isStructureInheriting("ExampleStructure1", "ExampleStructure2") ? "true" : "false"));
console.log((crxOop.isStructureInheriting("ExampleStructure2", "ExampleStructure1") ? "true" : "false"));
console.log((crxOop.isStructureInheriting("ExampleStructure3", "ExampleStructure2") ? "true" : "false"));
console.log((crxOop.isStructureInheriting("ExampleStructure3", "ExampleStructure3") ? "true" : "false"));
console.log((crxOop.isStructureInheriting("ExampleStructure1", "ExampleStructure3") ? "true" : "false"));
true
true
false
false
Notice how a structure is not inheriting itself. If you also want this case to return true, check the next section on crxOop.isStructureContaining().

7.6 crxOop.isStructureContaining
crxOop.isStructureContaining can be used during run time to inspect if pure instances of a structure are a super set of another structure. The function behaves exactly like crxOop.isStructureInheriting, but will return true if a structure is checked against itself. If any of the parameters is not a valid structure definition object, or registered structure name, crxOop.isStructureContaining will issue a fatal error.
crx_registerStructure("ExampleStructure1",
{
});
crx_registerStructure("ExampleStructure2",
{
INHERITS: ["ExampleStructure1"]
});
crx_registerStructure("ExampleStructure3",
{
INHERITS: ["ExampleStructure2"]
});
console.log((crxOop.isStructureContaining("ExampleStructure1", "ExampleStructure2") ? "true" : "false"));
console.log((crxOop.isStructureContaining("ExampleStructure2", "ExampleStructure1") ? "true" : "false"));
console.log((crxOop.isStructureContaining("ExampleStructure3", "ExampleStructure2") ? "true" : "false"));
console.log((crxOop.isStructureContaining("ExampleStructure3", "ExampleStructure3") ? "true" : "false"));
console.log((crxOop.isStructureContaining("ExampleStructure1", "ExampleStructure3") ? "true" : "false"));
true
true
true
false
crx_registerStructure("ExampleStructure1",
{
"VERBOSE": 1
});
crx_registerStructure("ExampleStructure2",
{
"VERBOSE": 1,
INHERITS: ["ExampleStructure1"]
});
crx_registerStructure("ExampleStructure3",
{
"VERBOSE": 1,
INHERITS: ["ExampleStructure2"]
});
console.log((crxOop.isStructureContaining("ExampleStructure1", "ExampleStructure2") ? "true" : "false"));
console.log((crxOop.isStructureContaining("ExampleStructure2", "ExampleStructure1") ? "true" : "false"));
console.log((crxOop.isStructureContaining("ExampleStructure3", "ExampleStructure2") ? "true" : "false"));
console.log((crxOop.isStructureContaining("ExampleStructure3", "ExampleStructure3") ? "true" : "false"));
console.log((crxOop.isStructureContaining("ExampleStructure1", "ExampleStructure3") ? "true" : "false"));
true
true
true
false

7.7 crxOop.isStructureRegistered
crxOop.isStructureRegistered can be used to check during run time whether a structure has been registered or not. The first parameter can be either a structure definition object (object), or a structure name (string).
crx_registerStructure("ExampleStructure1",
{
});
crx_registerStructure("ExampleStructure2",
{
});
console.log((crxOop.isStructureRegistered("ExampleStructure1") ? "true" : "false"));
console.log((crxOop.isStructureRegistered("ExampleStructure2") ? "true" : "false"));
console.log((crxOop.isStructureRegistered("ExampleStructure3") ? "true" : "false"));
true
false
crx_registerStructure("ExampleStructure1",
{
"VERBOSE": 1
});
crx_registerStructure("ExampleStructure2",
{
"VERBOSE": 1
});
console.log((crxOop.isStructureRegistered("ExampleStructure1") ? "true" : "false"));
console.log((crxOop.isStructureRegistered("ExampleStructure2") ? "true" : "false"));
console.log((crxOop.isStructureRegistered("ExampleStructure3") ? "true" : "false"));
true
false

8.0 Uitility Functions
CrxOop provides utility functions to help in developing with it. These utility functions are not to be confused with general utility functions that can be replaced wih similar functions from other libraries. These functions perform tasks in relation to CrxOop.

8.1 crxOop.bindFunction
crxOop.bindFunction() is a more general form of the javascript bind function, that should work on older browsers as well. crxOop.bindFunction() allows you to fine tune the bind result. Functions bound with crxOop.bindFunction() are aware of the internal CrxOop halt signal, and can also be protected from double binding, as well as other things.
Defaults to false.
Default to false.
Defaults to 0.
Defaults to null.
// To create a replacement roughly equivilant to the built in
// bind function when it is not available, one could use:
if(!Function.prototype.bind)
{
Function.prototype.bind = function(pThis)
{
return crxOop.bindFunction(this,
pThis, false, true, -1,
Array.prototype.slice.call(arguments, 1));
};
}

8.2 crxOop.var
crxOop.var() takes a single parameter, and if the parameter is not a function, returns it. If it is a function, it returns the function such as its 'this' is set to null. Internaly, crxOop.var() uses crxOop.bindFunction(), and will only bind the function if has not been bound to null before using crxOop.var() or crxOop.bindFunction(). If crxOop.var() binds the function, it does it by making the call crxOop.bindFunction(passedInParameter, null, false, true, -1, null).

8.3 crxOop.setTimeout
Equivilant to the javascript setTimeout function, with the added benefit that the timeout will not fire if CrxOop halts.

8.4 crxOop.setInterval
Equivilant to the javascript setInterval function, with the added benefit that the interval will halt if CrxOop halts.

9.0 Node.js Usage
CrxOop V2.x gives explicit support to Node.js. You can use the npm package, "crx-oop", or simply download CrxOop and include it using 'require()'. The returned object from require will be what is refered to as 'crxOop' in this documentation. For example, "crxOop.isStructureInheriting" would be found on this returned object as "isStructureInheriting". Further more, the global functions, crx_new, crx_registerClass, crx_registerInterface, crx_registerStructure and crx_static, will also be found on this object.
If you wish to have the globals be globals like in the browser, set a 'window' object on 'global'. If CrxOop finds one, it will populate it like in the browser, including with the object 'crxOop'. Doing this will not change what is returned when calling require. That behavior remains the same.
Be careful with your own js files. Node.js does not guarantee executing a js file only once, and hence you must make sure that you do not register the same Class, Interface, or Structure more than once with CrxOop. See "crxOop.isClassRegistered" and "crxOop.isStructureRegistered".

10.0 Security
CrxOop uses javascript features to provide the OOP features CrxOop provides. It is not a new language, and hence, it is susceptable to javascript's own features, whether good or bad. The 'instances' CrxOop creates are just javascript objects, and unless you are careful about what you are doing, the effect could break, which will break the integrity of the application, and break security when it comes to secure applications.
CrxOop instances, as mentioned earlier, are made of private memory sectors, and public memory sectors. The private sectors contain the class members declared as private, and the public sectors contain the class members declared as public. In other words, unlike other languages where private and public accessors are enforced by the compiler or interpretor, CrxOop's enforcement happens during runtime by carefull memory layout.
Two issues are to be kept in mind. One, the built instances could be modified during runtime, changing them from the class imprints that they are made from. Imagine passing a password to a secure function on an instnace, where the function is not the function you think it is. This issue is resolved by CrxOop using javascript's own object locking mechanisms. Needless to say, older browsers, roughly IE8 era, might suffer. However, exploiting this on the older browsers is not an easy matter, and with carefull programming should be impossible. In our example, making the secure function private is all that is needed. Please also see the section on CrxOop modes.
The second issue is leaking of the private memory sectors of an instance. This can happen through either passing of dangerous Class Keywords, including 'this', to other functions. Please see the section on Class Keywords for more information. Or infestation of the private sector by assigning a malicious function to a class private instance variable. Consider the following:
crx_registerClass("ExampleClass",
{
PRIVATE:
{
VARS:
{
"privateVar1": "privateVar1",
"privateVar2": null
}
},
PUBLIC:
{
FUNCTIONS:
{
"setPrivateVar2": function(pFunction)
{
this.privateVar2 = pFunction;
},
"printPrivateVar": function()
{
console.log(this.privateVar1);
},
"test": function()
{
this.privateVar2();
}
}
}
});
var vInstance = crx_new("ExampleClass");
vInstance.printPrivateVar();
//START: ATTACKER CODE
function maliciousCode()
{console.log(this.privateVar1);}
vInstance.setPrivateVar2(maliciousCode);
//END: ATTACKER CODE
vInstance.test();
privateVar1
crx_registerClass("ExampleClass",
{
"VERBOSE": 1,
"private var privateVar1": "privateVar1",
"private var privateVar2": null,
"public function setPrivateVar2": function(pFunction)
{
this.privateVar2 = pFunction;
},
"public function printPrivateVar": function()
{
console.log(this.privateVar1);
},
"public function test": function()
{
this.privateVar2();
}
});
var vInstance = crx_new("ExampleClass");
vInstance.printPrivateVar();
//START: ATTACKER CODE
function maliciousCode()
{console.log(this.privateVar1);}
vInstance.setPrivateVar2(maliciousCode);
//END: ATTACKER CODE
vInstance.test();
privateVar1
Notice how the malicious function maliciousCode() managed to read the private variable privateVar1, and all it took was the reading of "this". Very serious, but maybe not so because exploiting this can be difficult. The attacker still needs to access the private variable where his malicious code was stored, which in our case was conveniently executed by the test function. However, this author does not know if there are other ways to exploit this, but one thing for certain, if not a security risk, this can lead to bad habbits. Avoid exploiting this yourself on your own classes to try and change the meaning of accessors.
When it comes to security, despite the above skepticism, do not play with fire. And when it comes to integrity, avoid exploiting this with your own code to keep code well defined and clean. To stop such things, CrxOop provides the function crxOop.var(). crxOop.var takes a single argument, and if it is a function returns a safe modification of the function, and if not returns the argument as it is.
crx_registerClass("ExampleClass",
{
PRIVATE:
{
VARS:
{
"privateVar1": "privateVar1",
"privateVar2": null
}
},
PUBLIC:
{
FUNCTIONS:
{
"setPrivateVar2": function(pFunction)
{
//SECURITY
this.privateVar2 = crxOop.var(pFunction);
},
"printPrivateVar": function()
{
console.log(this.privateVar1);
},
"test": function()
{
this.privateVar2();
}
}
}
});
var vInstance = crx_new("ExampleClass");
vInstance.printPrivateVar();
//START: ATTACKER CODE
function maliciousCode()
{console.log(this.privateVar1);}
vInstance.setPrivateVar2(maliciousCode);
//END: ATTACKER CODE
vInstance.test();
undefined
crx_registerClass("ExampleClass",
{
"VERBOSE": 1,
"private var privateVar1": "privateVar1",
"private var privateVar2": null,
"public function setPrivateVar2": function(pFunction)
{
//SECURITY
this.privateVar2 = crxOop.var(pFunction);
},
"public function printPrivateVar": function()
{
console.log(this.privateVar1);
},
"public function test": function()
{
this.privateVar2();
}
});
var vInstance = crx_new("ExampleClass");
vInstance.printPrivateVar();
//START: ATTACKER CODE
function maliciousCode()
{console.log(this.privateVar1);}
vInstance.setPrivateVar2(maliciousCode);
//END: ATTACKER CODE
vInstance.test();
undefined
The code works now as expected. crxOop.var simply binds functions' 'this' to something safe. You can use javascripts own facilities if you like, but the advantage to crxOop.var is that it works with older browsers, and it makes the functions work with CrxOop's internal halt signal. This means, if there is a fatal error issued by CrxOop, those functions will also halt.

11.0 License
The MIT License (MIT)
Copyright (c) 2016 ObIb
ObIb: Ob are the first two letters of my first name, and Ib are the first two letters of my father's father's (grandfather's) first name. I was the senior developer at Creatrix Design Group (website: creatrix.ca. address:2764 Richmond Rd, Ottawa Ontario Canada K2B6S2), during but not limited to the year 2014. This information is part of the license and uniquely identifies me, the license holder.
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.