Types and Variable Declarations
This document describes how types and declarations work in Ejscript. This is largely governed by the ECMAScript standard, but depending on the Ejscript Language Compliance Mode, variable declarations, scope and visibility may operate slightly differently. The differences are documented below.
System Type Library
Ejscript defines a set of core system types to provide a powerful and flexible system library. The types provide a large selection of methods to manage and operate on the types. The primitive types (booleans, numbers, objects, regular expressions and strings) are instances of these types.
The type system is also extensible. Users can create new types by scripting or by creating native types in the C language.
The Ejscript system library consists of three groups of types:
- Standard JavaScript types
- Ejscript extension types
- Ejscript Web Framework types
Standard Types
The core types consist of the standard JavaScript types: Array, Boolean, Date, Error, Function, Number, Object, RegExp and String.
Ejscript Extension Types
Ejscript adds a library system types: BinaryStream, ByteArray, Dispatcher, Event, File, Http, Iterator, Namespace, Null, Reflect, Socket, TextStream, Type, Url, XMLHttp, and Void. These types add support for file and network IO, eventing and listening, byte data access and management and introspection via type reflection.
The Ejscript web framework provides a Model/View/Controller framework and adds these types: Application, Controller, Cookie, Database, GoogleConnector, Host, HtmlConnector, Record, Request, Session, UploadFile, View and ViewConnector.
Variable Declarations
Variables are declared by the var or let keyword. The var keyword will define a variable in the top level of the current scope whereas a let variable declaration will define a variable in the current block scope. In other words, var declarations will hoist to the top-most block in a function/class or global script, whereas a let variable will be scoped to the block in which it is declared.
function test() { x = 0 // Assign zero to the "x" created by the var declaration below { // Create an inner block let x = 2 // This creates a new, local "x" print(x) // Prints the local "x" } // The inner "let x" disappears from here var x // Define "x", exists from start of function print(x) // Print the outer "x" } test() 2 0
The var declaration above defines a variable "x" that exists from the start of the function. This is because var declarations hoist to the top of the function. Whereas, the let declaration of x is local to the block and creates a new "x" that only exists inside the block.
Variable Scope
Variables can be defined in several locations. Depending on the location, the variable declarations behave differently. The locations are:
- Global
- Class Static
- Class Instance
- Function Local
- Block Local
All variables declared by using let will be block local and are scoped to the block enclosing its declaration.
Global Variables
Variables declared with var and outside classes, interfaces and functions are global variables. In Ejscript, global variables are by default limited in their visibility to the source file in which they are declared. If the variable is defined with a public qualifier, the variable will be visible to code defined in other source files.
Class Static Variables
Variables declared inside classes and decorated with the static keyword will be class variables and will be shared among all instances of the class. The visibility of class variables can be modified by using visibility qualifiers.
Class Instance Variables
Variables declared inside classes and not decorated with the static keyword will be class instance variables. Each object instance of the class will have its own copy of the variable. The visibility of instance variables can be modified by using visibility qualifiers.
Function Local Variables
Variables declared using var inside functions, are local variables. They will be re-created each time the function is executed and are declared at the top-most block of the function.
Block Variables
Variables declared with let are local to the block enclosing the declaration.
Controlling Variable Visibility
Standard JavaScript provides little control over variable visibility and this is a problem as programs grow, or as source code from multiple sources is integrated or combined. Ejscript addresses these issues in two ways:
- Visibility qualifiers
- Module directives
Visibility Qualifiers
Classes, global variables and function declarations can optionally be qualified as either: internal or public.
An internal qualifier, which is the default, defines a variable or function to be visible only within the defining source file. A public qualifier enables the variable or function to be visible to code in other source files.
public var message = "Hello" public function greeting() { print(message) } internal function privateGreeting() { print(message + " Friends") } public class Friends() { }
Variables or functions inside a class can also be qualified with private which makes the declaration private to the class and invisible to outsiders. Similarly, protected will make the declaration visible inside the class and to any sub-classes.
Modules
Ejscript provides a powerful module facility to control the visibility of a set of classes, variables or functions.
The module directive groups and qualifies a set of declarations so that they will not conflict or collide with other names.
module acme.rockets { var version = "1.0" function BlastOff() { } }
This creates a module named "acme.rockets" and prefixes declarations with the module name.
It is important that you choose a unique module name that won't clash with other modules. A reverse domain name is one safe option. For example: "com.embedthis.appweb".
To use a access a module, users need to add a require directive to their code.
require acme.rockets BlastOff()
The require directive adds the "acme.rockets" module to the set of open modules and makes visible the declarations defined in the module.
When creating modules, multiple source files can contribute code into the same module. The Ejscript compiler will aggregate the code and combine it into a single module.
Undeclared Variables
It is is not essential to declare variables before using them. If you assign a value to an undeclared variable, the Ejscript virtual machine will dynamically create the variable when it is first assigned. In standard JavaScript, assigning to an such an undeclared variable will create a new global variable. In other words, forgetting to declare a variable, even when done in function code, will transparently create an unintended global declaration.
Ejscript mode treats assignments to undeclared variables differently by creating local variables instead of global variables. This is much safer and more secure. If you really need to create a global variable, you can use the global object.
global.name = 7
This will create a new global variable called globalVar not matter where and what scope this code is executed in.
Optional Type Annotations
In JavaScript, variables are not explicitly typed. The type of a variable is defined by the value of the data assigned to the variable. Consequently, variables can change their type at run-time. One minute, they can contain an integer, and the next, a string. This is not necessarily bad. In fact, it can be very useful. However, there are times when you want to control the type and prevent such type changes.
Ejscript optionally allows variables to be defined with an type annotation. You can choose to either specify the type of the variable by appending ": TypeName" or leave the declaration untyped — it is your choice. Adding type declarations makes your intent explicit as to the range of values a variable can take. The compiler and the virtual machine can then enforce that choice and automatically convert values or trap errors if an incompatible value is assigned to the variable. For example:
x = "Hello World" // Auto create a variable "x" var y = 7 // Declare "y", but leave it untyped then assign 7 var z: Number // Type annotated. Declare "z" and type as a Number y = x // "y" now becomes a string. This is Ok. z = "Not allowed" // The will be prevented this because "z" is typed as a Number
When this script is compiled, the compiler will define two variables "y" and "z". The "z" variable declaration will be typed to only hold numeric values. When the script is later run, Ejscript will define the variable x and set its value to "Hello World". It will then assign the number 7 to "y" and then will copy the string in "x" into "y" effectively changing its type. Lastly, an attempt to assign "Not allowed" into the variable "z" will be caught and will throw an exception.
Judicious use of type annotations can increase the performance of the generated byte code. When the compiler knows the type, it can often bind to the actual properties and methods on that variable. Optional type annotation is also very useful for API interfaces and when generating documentation.
The Ejscript documentation generation tool (ejsmod) uses type annotations when documenting APIs. But remember, no-one is forcing you to use type annotation. Use it if it helps your code. You can also add type annotations incrementally as you develop and your design solidifies.