ECMAScript 4 Netscape Proposal
Core Language
Variables
previousupnext

Wednesday, June 4, 2003

Variable Definitions

VariableDefinition  VariableDefinitionKind VariableBindingList
VariableDefinitionKind 
   var
|  const
VariableBindingList 
   VariableBinding
|  VariableBindingList , VariableBinding
VariableBinding  TypedIdentifier VariableInitialisation
VariableInitialisation 
   «empty»
|  = VariableInitializer
VariableInitializer 
   AssignmentExpression
|  AttributeCombination
TypedIdentifier 
   Identifier
|  Identifier : TypeExpression

A SimpleVariableDefinition represents the subset of VariableDefinition expansions that may be used when the variable definition is used as a Substatement instead of a Directive in non-strict mode. In strict mode variable definitions may not be used as substatements.

SimpleVariableDefinition  var UntypedVariableBindingList
UntypedVariableBindingList 
   UntypedVariableBinding
|  UntypedVariableBindingList , UntypedVariableBinding
UntypedVariableBinding  Identifier VariableInitialisationallowIn

A variable defined with var can be modified, while one defined with const is read-only after its value is set. Identifier is the name of the variable and TypeExpression is its type. Identifier can be any non-reserved identifier. TypeExpression must be a compile-time expression that evaluates to a type t other than Never. TypeExpression may contain forward references to compile-time constants defined later in the program.

If provided, AssignmentExpression gives the variable’s initial value v. If AssignmentExpression is not provided in a var definition, then undefined is assumed. If the variable being defined is not an instance member of a class, then the AssignmentExpression is evaluated at the time the variable definition is evaluated. The resulting value is then implicitly coerced to the variable’s type t and stored in the variable. If the variable is defined using var, any values subsequently assigned to the variable are also implicitly coerced to type t at the time of each such assignment. If the variable is an instance member of a class, then the AssignmentExpression is evaluated each time an instance of the class is constructed.

Reading or writing a variable before its definition is evaluated signals an error except when the variable definition has no attributes and no type and strict mode is not in effect; in that case, the variable may be read or written prior to its definition being evaluated and its initial value is undefined.

Multiple variables separated by commas can be defined in the same VariableDefinition. The values of earlier variables are available in the AssignmentExpressions of later variables.

If omitted, TypeExpression defaults to type Object. Thus, the definition

var a, b=3, c:Integer=7, d, e:Integer, f:Number=c;

is equivalent to:

var a:Object = undefined;
var b:Object = 3;
var c:Integer = 7;
var d:Object = undefined;
var e:Integer = undefined;   // Implicitly coerced to NaN
var f:Number = c;            // 7

The compiler might issue a warning for a VariableDefinition that contains an untyped variable prior to a typed variable to remind programmers that the type of d is Object rather than Integer in var d, e:Integer.

Constant Definitions

const means that assignments to Identifier are not allowed after the constant has been set. Once defined, a constant cannot be redefined in the same scope, even if the redefinition would be to the same value. If the VariableBinding in a const declaration does not contain an initializer, then the constant may be written once after it is defined. Any attempt to read the constant prior to writing its value will result in an error. For example:

function f(x) {return x+c}

f(3);           // Error: c’s value is not defined
const c = 5;
f(3);           // Returns 8
const c = 5;    // Error: redefining c

Just like any other definition, a constant may be rebound after leaving its scope. For example, the following is legal; j is local to the block, so a new j binding is created each time through the loop:

var k = 0;
for (var i = 0; i < 10; i++) {
  const j = i;
  k += j;
}

A const definition defines a compile-time constant if it has an initializer and that initializer is a compile-time constant expression which may contain forward references to other compile-time constants.

In order for the compiler to be able to distinguish const definitions that define run-time constants from ones that define compile-time constants, it must be able to resolve each variable referenced in a const initializer to a scope. Because of this, a const initializer may only refer to variables declared at compile time; referring to a dynamically created variable not declared at compile time results in a compile-time error. It is also an error to attempt to create a dynamic variable that changes the resolution of a variable in a const initializer. For example:

const a = 5;      // OK: a is a compile-time constant with the value 5
const b = a + c;  // 
OK: b is a compile-time constant with the value 7
const c = 2;      // 
OK: c is a compile-time constant with the value 2
const d = e;      // 
OK: d is a run-time constant that starts as undefined
var e = 2.718281828459045;
f = "Run time";
const g = f;      // 
Error: f is not declared at compile time
const h = uint;   // 
OK: h is a compile time constant that holds the type uint
this.ulong = 15;  // 
OK: creates the global property ulong that shadows the system ulong type
this.uint = 15;   // 
Error: can’t create the global property uint because it would
                  // 
  change the resolution of uint in the definition of h

Inside a class, const preceded by final defines an instance member. Preceding it with virtual would also define an instance member, but is only useful if one wants subclasses to be able to override the constant. Precede it with static to define a global member. The default is final.

If const is declaring an instance member m of a class, then the initializer is evaluated each time an instance of the class is constructed. If absent, then the member’s property may be written exactly once, cannot be re-written after it has been written, and must be written before it can be read. For example:

class C {
  static const red = 0xFF0000;      // Defines static constant C.red
  static const green = 0x00FF00;    // Defines static constant C.green
  static const blue = 0x0000FF;     // Defines static constant C.blue
  static const infrared;            // Defines uninitialized static constant C.infrared

  const myColor;                    // Defines instance constant C::myColor with value set by the constructor
  final const yourColor;            // Defines instance constant C::yourColor with value set by the constructor
  const ourColor = 0;               // Defines instance constant C::ourColor that is always zero (not very useful)
  virtual const theirColor = 0;     // Defines instance constant C::theirColor that can be overridden by subclasses

  function C(x:int) {
    myColor = x;                    // Sets this instance’s myColor
    ourColor = x;                   // Error: ourColor is already set to 0
    myColor = x;                    // Error: myColor can be set only once
    var a = [x, this];
    a[1].yourColor = x;             // Sets this instance’s yourColor
  }
}

Getters and Setters

A definition var x:t = v internally creates a hidden variable and defines a getter and a setter to access that variable:

A definition const x:t = v internally creates a hidden variable and defines a getter to access that variable:

This relationship between a variable and its getter and setter is normally transparent but can be exploited occasionally. For instance, a variable can be declared that is private for writing but public for reading:

private var name:String;
public export get name;

A subclass may override a variable’s getter or setter. To do this, the original variable has to be declared non-final because variables are final by default:

class C {
  virtual var x:Integer;
  var y:Integer;
}

class D extends C {
  override function set x(a:Integer):Integer {y = a*2}
}

var c = new C;
c.x = 5;
c.x;       // Returns 5
c.y;       // Returns NaN (the default value for an Integer variable)
var d = new D;
d.x = 5;
d.x;       // Returns NaN
d.y;       // Returns 10

Waldemar Horwat
Last modified Wednesday, June 4, 2003
previousupnext