CrxOop CrxOop: Bringing Object Oriented Programming, and Proper Prototype Based Programming, To Javascript
The aim of the library documented below is to provide developers with a solution to allow development using interfaces and classes as they are known in other object oriented programming (OOP) languages such as C++, C#, Java and PHP. Further more, V1.4 introduces structures, a generalization of the concept of prototypal inheritance, known here as POBP.
Subject
Body*
Email
SUBMIT CLOSE
Menu

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:

  1. 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'.
  2. 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.
  3. 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'.
  4. The process repeats until all constructors are called.
  5. The instance is now locked. This protects from new properties being created on the underlying javascript object.

Consider the following code and output:

Instance Construction
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 ClassC using pA = 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 ClassB using pA = 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 ClassA using pA = 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.

Instance Construction
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);
Creating Instance of ClassB1:
CONSTRUCTING using pA = undefined
Creating Instance of ClassB2:
CONSTRUCTING using pA = 5