ECMAScript 4 Netscape Proposal
Core Language
Variables
|
Wednesday, June 4, 2003
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.
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
.
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
OK:
const b = a + c; // b
is a compile-time constant with the
value 7
OK:
const c = 2; // c
is a compile-time constant
with the value 2
OK:
const d = e; // d
is a run-time constant that
starts as undefined
Error:
var e = 2.718281828459045;
f = "Run time";
const g = f; // f
is not declared at compile
time
OK:
const h = uint; // h
is a compile time constant that holds the
type uint
OK: creates the global property
this.ulong = 15; // ulong
that shadows the system
ulong
type
Error: can’t create the global property
this.uint = 15; // 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
}
}
A definition var
x:
t =
v internally
creates a hidden variable and defines a getter and a setter to access that variable:
undefined
to type t
(such a coercion must exist for every type) and assign the result to .function get
x():
t {return
}
.function set
x(a:
t):Void {
= a}
.A definition const
x:
t =
v internally
creates a hidden variable and defines a getter to access that variable:
function get
x():
t {return
}
.function set
x(a:
t):Never {throw
ConstWriteError}
.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 |