The keyword namespace is used to declare new namespaces. A namespace is a collection of functions, classes, variables, etc. aggregated in a single unit. Namespaces can also contain other (inner) namespaces. Under the hood, a namespace is just a named global object. The keyword super can be used to access members of the parent namespace that have the same name as those in the inner namespace.
Declaration of a namespace:
namespace <name>{
[ <function> | <var> | <const> | <class> | <namespace> ]*}
The keyword class is used to define new classes (types of objects):
class <class-name> [ ( : | extends ) <parent-class name> ] { [ <function> | <constructor> | <property> | <var> | <const> | <this var> | <class> ]* }
<function>
is a standard function declaration. Such functions can be used as instance or static methods of the class.function
keyword.<constructor>
is a function with predefined name this or constructor (ES2015 compatibility) .<property>
is a special type of member function. See more details below.<var>
is a standard var declaration - allows to define class or module variables (a.k.a. static variables)<const>
is a declaration of named constant.<class>
is a declaration of inner class.<this var>
is a declaration of member variable[s]:this var <name> [= <value>] [, <name> = <value> , ...];
The function with the name this, declared in the context of a class, serves the role of the function-constructor for all instances of the class:
class Tor
{
this var one = 1; // declaration of member variable
function this() { this.two = 2; } // constructor
// or (ES2015 compatibility) as
constructor() { this.two = 2; } // constructor declaration, function omitted
}
The main goal of the constructor is to initialize new instances of the class (a.k.a. objects) by creating and initializing all the fields that the instance shall contain. The object that is being created is accessible through the variable named this inside the constructor 1).
The compiler invokes the constructor when it handles the new operator:
var tor = new Tor();
// tor.one == 1 and
// tor.two == 2 at this point
In any other aspect, function this()
is an ordinary function that may have any number of parameters defined, including the optional parameters.
A function with any other name, declared in the context of a class, is known as a method of that class. If a method has a reference to the this variable, it is called an instance method (a.k.a. dynamic). If it does not refer to the this variable, it is a class method (a.k.a. static). In principle, the same function can be used as both, an instance and a class method. When invoked as a class method, this will have a reference to the class or namespace where the method is defined. When invoked as an instance method ( e.g. obj.method() ) this will be equal to the object instance (obj in this case).
For the compatibility with ES2015 (a.k.a. JavaScript 6) function
keyword can be omitted from function declarations.
Instance methods must be called in the context of particular object of the class:
class Tor
{
function rotate( angle ) { this.angle = angle; this.update(); }
function update() { ... }
}
var tor = new Tor();
tor.rotate(270); // Tor.rotate() method called in the context
// of a particular instance - 'tor'
Or in ES2015 style:
class Tor { rotate( angle ) { this.angle = angle; this.update(); } update() { ... } } var tor = new Tor(); tor.rotate(270); // Tor.rotate() method called in the context // of a particular instance - 'tor'
Static method of a class or namespace can be called by using the ClassOrNamespace.Method notation (a.k.a. the "dot notation"):
class Tor
{
var all = [];
function rotateAll( angle ) { for(var t in Tor.all) t.rotate(angle); }
}
Tor.rotateAll(270); // Tor.rotateAll() method called as a class method
Sciter script supports definitions of computable (a.k.a. virtual) properties. These are special functions that are executed to set or get the value of a property. The syntax of a computable property definition:
property <function-name> ( <parameter> )
{
[ <statement> | get <statement> | set <statement> ]*
}
Alternative form introduced for ES2015 compatibility are get/set member functions:
get <property-name> ( ) { [ <statement> ]* } set <property-name> ( val ) { [ <statement> ]* }
The body of the property
function can contain statements that may appear in a normal function body and also the special get and set sections. The get section (one or more) is executed conditionally when the function is invoked for getting the property value, and the set section for setting the property value. Example:
class Baz
{
function this() { this._first = 1; this._second = 2; }
property first( val )
{
get return this._first; // single expression get block
set this._first = val; // single expression set block
}
property second( val )
{
stdout << "second";
get
{
return this._second + 2;
}
set
{
this._second = val - 2;
}
}
}
val
.ES2015 compatible alternative declaration of the above:
class Baz { constructor() { this._first = 1; this._second = 2; } // single expression get/set functions get first() return this._first; set first(val) this._first = val; get second() { stdout << "second"; return this._second + 2; } set second(val) { stdout << "second"; this._second = val - 2; } }
It is possible to define a so-called Undefined Property Handler, which deals with undefined properties. The syntax of the UPH is similar to the computable property handler, except for the undefined name token used in its name:
property undefined ( <name>, <value> ){
[<statement> | get <statement> | set <statement> ]*}
The UPH function must have precisely two parameters: the name of the property, and its value (used in the property set operation). UPH's can be used in cases where the set of properties is unknown upfront. For example, you may wish to access the fields of a recordset as properties:
property Recordset.undefined(name, val)
{
get return this.getFieldValue(name);
}
...
var rs = DB.exec("SELECT firstName, secondName FROM employee");
var n1 = rs.firstName; // redirect to rs.getFieldValue(#firstName); call
var n2 = rs.secondName; // through property undefined()
Class, or namespace, variables are defined in the context of a class or namespace by using the var keyword. Example:
namespace Toz
{
var all = []; // create variable that contains empty array;
var singleton = { something: 123; }; // create variable
// that contains object;
var counter = 0; // create variable that contains integer zero;
}
To access such variables, use their fully qualified names:
Toz.all.push("anything");
Toz.counter += 1;
Classes may have so called instance variables defined upfront. Instance variable is unique to each instance of the class - not shared with other instances.
Instance variables are defined in context of a class by using this var keyword combination. Example:
class Recordset { this var currentRecord = []; // note: these are this var dbCursor = null; // instance variables function next() { this.currentRecord = DB.advanceNext(this.dbCursor); } }
Namespace constants (a.k.a. constants) are read-only variables, defined in the context of a type by using the const
keyword. Example:
namespace Toz
{
const ALIGN_LEFT = 1;
const ALIGN_CENTER = 2;
const ALIGN_RIGHT = 3;
}
To access such constants outside of class or namespace functions, use their fully qualified names using dot notation:
switch(alignment)
{
case Toz.ALIGN_LEFT: ... break;
case Toz.ALIGN_CENTER: ... break;
case Toz.ALIGN_RIGHT: ... break;
default: ... break;
}
Inside classes and namespaces constants can be used directly without dot notation.
1) The idea for using this for a constructor name was borrowed from the D language