|
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 5OK:
const b = a + c; // b is a compile-time constant with the
value 7OK:
const c = 2; // c is a compile-time constant
with the value 2OK:
const d = e; // d is a run-time constant that
starts as undefinedError:
var e = 2.718281828459045;
f = "Run time";
const g = f; // f is not declared at compile
timeOK:
const h = uint; // h is a compile time constant that holds the
type uintOK: creates the global property
this.ulong = 15; // ulong that shadows the system
ulong typeError: 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 |