July 2000 Draft
JavaScript 2.0
Core Language
Definitions
|
Tuesday, June 6, 2000
Definitions introduce new constants, variables, functions, classes, interfaces, and namespaces. All definitions can be preceded by zero or more attributes using the following syntax:
An attribute is an identifier that modifies a definition. Attributes can specify a definition's scope, namespace, semantics, and other hints. A JavaScript program may also define and subsequently use its own attributes.
The table below summarizes the predefined attributes.
Category | Attribute | Behavior |
---|---|---|
Scope | local |
The definition is local in the enclosing block. |
scope |
The definition applies to the enclosing scope. | |
global |
The definition applies to the enclosing package. | |
Namespace | private |
If the definition's scope is a class or interface, makes the definition visible only in the class's or
interface's private namespace; otherwise, behaves the same as package . |
package |
Makes the definition visible only in the package's private namespace. | |
public |
Makes the definition visible anywhere. | |
Visibility Modifiers | implicit |
This definition can be made available as a variable using a use statement. |
explicit |
This definition cannot be made available as a variable using a use statement. |
|
indexable |
This definition can be accessed using the [] operator. |
|
nonindexable |
This definition cannot be accessed using the [] operator. |
|
enumerable |
This definition can be seen using the for -in statement. |
|
nonenumerable |
This definition cannot be seen using the for -in statement. |
|
Semantic | static |
The definition creates a global member (rather than an instance member) of the enclosing class or interface. The definition's scope must be a class or interface. |
instance |
The definition creates an instance member (rather than a global member) of the enclosing class or interface. The definition's scope must be a class or interface. | |
final |
The definition cannot be overridden in subclasses. The definition's scope must be a class or interface. | |
virtual |
The definition can be overridden in subclasses using an identical type signature only. The definition's scope must be a class or interface. | |
volatile |
The definition can be overridden in subclasses using any type signature. The definition's scope must be a class or interface. | |
abstract |
No definition is supplied for this function in this class or interface. Calling this function throws an exception unless the function is overridden in a subclass. | |
concrete |
The opposite of abstract . |
|
traditional |
Allows a function to access this and be used as a constructor even though it is not an instance
method. See traditional functions. |
|
Hint | override |
The definition overrides a member of a superclass. |
mayOverride |
The definition may override a member of a superclass. | |
compile |
Compiler hint that the definition may be processed at compile time. | |
unused |
Compiler hint that the definition is not used. |
If multiple conflicting attributes are used, the latter ones take precedence, so static
final
instance
is the same as final
instance
.
A scope attribute describes the scope to which a definition applies.
The local
attribute applies the definition to the enclosing Block.
If the enclosing block is a class or interface, the definition does not appear as a member of that class or interface. If
the enclosing block is a package, the definition does not appear as a member of that package, so other packages importing
this one will not see this definition.
The scope
attribute applies the definition to the enclosing scope. If the
enclosing scope is a class, interface, or package, the definition will appear as a member of that class, interface, or package.
The global
attribute applies the definition to the enclosing package. The definition will appear as a member
of that package.
Unless changed, the default scope is scope
.
Namespace attributes control the definition's visibility. User-defined attributes provide a finer grain of visibility control.
Every package has a predefined, anonymous namespace PackageSecrets. That namespace is attached to all definitions
with the package
attribute in that package. The package's scope includes an implicit use
namespace
PackageSecrets statement around the package that grants access to these definitions from within the package only.
Every class and interface has a predefined, anonymous namespace ClassSecrets. That namespace is attached to
all definitions with the private
attribute in that class or interface. The class or interface's scope includes
an implicit use
namespace
ClassSecrets statement around the class or interface that grants
access to these definitions from within that class or interface only.
Unless changed, the default namespace is public
, which grants unrestricted access to use the definition. If
at least one namespace attribute is provided, then the default namespace is ignored. If more than one namespace attribute
is provided, then the definition's namespace is the union of the given namespaces and the definition can then be accessed
through any of these namespaces.
Visibility modifier attributes control the definition's visibility in several special cases.
explicit
and implicit
The explicit
modifier prevents the definition from being accessed as an unqualified variable through a use
statement. The definition can still be accessed using a namespace qualifier or by accessing it as a member of an object.
For example,
package P1 { const c1 = 5; explicit const c2 = 7; } package P2 { use import P = P1; // Imports P1 and then uses it c1; // OK; evaluates to 5 c2; // Error: c2 not defined P.c2; // OK: member access of object P P::c2; // OK: package P used as a qualifier }
explicit
is used to add public
definitions to a package P without having them conflict
with definitions in other packages that import
and use
package P.
The implicit
modifier is the opposite of explicit
; implicit
is the default.
indexable
and nonindexable
An indexable
definition can be accessed using the default []
operator. A nonindexable
definition cannot be accessed using this operator. The default is indexable
for public
definitions and nonindexable
for non-public
definitions.
enumerable
and nonenumerable
An enumerable
definition can be seen by the for
-in
iteration statement. A nonenumerable
definition cannot be seen by such a statement. The default is enumerable
for public
definitions and nonenumerable
for non-public
definitions. enumerable
implies
indexable
.
The static
attribute makes the definition create a global member rather than an instance member of the enclosing
class or interface. The instance
attribute reverses this -- it makes the definition create an instance member
of the enclosing class or interface.
The final
attribute prevents subclasses from overriding this definition in the same namespace. virtual
allows subclasses to override this definition but requires the overriding definition to have exactly the same type signature
as this definition. volatile
allows subclasses to override this definition without type signature restrictions.
Unless changed, the default attributes are instance
volatile
concrete
for function
(method) definitions and instance
final
concrete
for other (constant, variable, and
class) definitions of a class's or interface's members.
These attributes may only be used on definitions that apply to a class or interface. They cannot be used on definitions that, for instance, create local variables inside a function.
The override
and mayOverride
attributes control warnings. Normally defining a class or interface
member with the same name as a visible member of a superclass generates a warning. The override
attribute reverses
the sense of the warning so that the warning will be generated if there is no visible member of a superclass with the same
name. The mayOverride
attribute turns off this warning altogether.
The compile
attribute is a hint that the definition may be evaluated early. See compiler
blocks.
The unused
attribute is a hint that the definition is not referenced anywhere. Referencing the definition
will generate a warning.
A user-defined attribute may be defined using the following syntax:
default
=
The first Identifier is the name of the attribute.
The list of attributes after the =
is its expansion; that list may be empty.
Identifier may be used as an Attribute
until the end of the enclosing block. The names of attributes are separate from and do not interfere with the names of variables,
functions, classes, etc. If an attribute is defined with a name that matches a non-reserved
keyword (other than get
or set
) then that keyword's
normal meaning becomes inaccessible until the end of the enclosing block. It's best to avoid such a conflict, but this rule
allows JavaScript to evolve by adding new non-reserved keywords without breaking existing programs that happen to use them
as attributes.
If namespace(
N)
is put on the right side of an AttributeDefinition,
then the newly defined attribute, when used, will attach definitions to namespace N. For security reasons namespace
N must be defined in the current package (if N were imported from another package P then
it might be possible to, for instance, override some of P's final
methods).
If default
is used instead of attribute
id, then the default attribute is changed
to the right side of the AttributeDefinition. This default
remains in place until another default attribute definition in the same block or the end of the block. The default does not
propagate into subblocks of the enclosing block.
As an example, the following code creates aliases priv
and loc
of the attributes private
static
and local
:
attribute spriv = private static; attribute loc = local; explicit namespace Version1; explicit namespace Version2 extends Version1; attribute V1 = namespace(Version1); attribute V2 = namespace(Version2); class C { priv var x; V1 var simple; V2 var complicated; priv const a:Array = new Array(10); priv instance var b; // Instance variable loc var i; for (i = 0; i != 10; i++) a[i] = i; }
Each defined name has a particular static and dynamic extent. The static extent is the region of source code where the name is visible (as long as the namespaces, if any, match and the name isn't shadowed by a more local definition). The dynamic extent is the time interval during which the defined constant, variable, function, class, or interface may be accessed.
Each definition or declaration D of a name n has an implicit or explicit scope attribute, which designates a scope A to which the definition applies. In general, n's static extent is the portion of scope A beginning with D and ending with the end of scope A; n is invisible from code located textually prior to D. If n is declared or defined several times inside a scope, its static extent begins with the first declaration or definition.
There are a few cases where an implicit declaration of n is inserted prior to the actual definition of n. These rules apply only if n is an unqualified name.
function
n ... definition is located at the top level of a block B, then an
implicit declaration const
n;
is inserted at the beginning of B.class
n ... definition is located at the top level of a block B, then an implicit
declaration class
n;
is inserted at the beginning of B.interface
n ... definition is located at the top level of a block B, then an
implicit declaration interface
n;
is inserted at the beginning of B.var
n;
definition without a type or attributes applies to a scope S, then an implicit declaration var
n;
is inserted at the beginning of S.The first three rules permit the definition of recursive functions, classes, and interfaces without predeclaring them first. The fourth rule is for compatibility with JavaScript 1.5.
A definition D of a name n may be invisible even inside its static extent either because another,
more local definition of n shadows D or because D specified a namespace that is not currently
use
d.
The dynamic extent of a definition or declaration D of a name n begins when D is executed. Each kind of a declaration and definition provides semantics that state how n may be subsequently accessed:
Kind | Declaration | Definition |
---|---|---|
const |
An attempt to read n generates an error until the first definition of n is executed. | n's value is now readable. |
var |
If there exists a coercion of undefined to n's type, n is set to the
appropriately coerced undefined ; otherwise, an attempt to read n generates an error until the
first definition of n is executed. In either case, n may be written. |
n's value is now readable and writable. |
function |
An attempt to read or call n generates an error until the first definition of n is executed. | n is now readable and callable. |
class |
n is now readable as a value of type type , but no other operations may be performed
on it. |
Instances of n may be created and methods of n may now be called. n may now be used as a base class. |
interface |
n is now readable as a value of type type , but no other operations may be performed
on it. |
Methods of n may now be called. n may now be used as a base interface. |
namespace |
n is now usable as a namespace. | |
export |
n is now usable in the same capacity as the original identifier. |
These rules also apply to the implicit declarations of functions, classes, interfaces, and variables.
A definition or declaration D of a name n is invisible prior to the beginning of its dynamic extent. If an attempt is made to access n in this situation, the enclosing scopes are searched instead for an appropriate definition, if any, of n. Contrast the following programs:
const b:Integer = 1; function f(c:Boolean):Integer { const a = b; const b; if (c) const b:Integer = 10; return a+b; }
In the above program, f(true)
returns 11
, while f(false)
throws an error in the
return a+b
statement because the inner b
has been declared but not defined.
const b:Integer = 1; function f(c:Boolean):Integer { const a = b; if (c) const b:Integer = 10; return a+b; }
In the above program, f(true)
returns 11
, while f(false)
returns 2
.
In the latter case, even though the return a+b
statement is in the static extent of the const b:Integer = 10
declaration, it is not in that declaration's dynamic extent.
In general, it is not legal to define the same entity twice within a scope A without exiting A in the interim. There are a few exceptions:
var
or const
definition may be executed repeatedly. Here "the same" means that the
definition is in the same location in the source code, which can happen if the definition is located inside a loop. Moreover,
the definition's type, if any, must not change each time the definition is executed, and, if the definition is of a const
,
then its value may not change either.var
definitions without a type or attributes may be executed repeatedly on the same variable.In the example below the comments indicate the scope and namespace of each definition:
var a0; //
Public global variable
local var a1; //
Package-visible global variable
private var a2 = true; //
Package-visible global variable
package var a3; //
Package-visible global variable
public var a4; //
Public global variable
global var a5; //
Public global variable
if (a2) {
var b0; //
Public global variable
local var b1; //
Local to this block
private var b2; //
Package-visible global variable
package var b3; //
Package-visible global variable
public var b4; //
Public global variable
public var b5; //
Public global variable
}
public function F() { //
Public global function
var c0; //
Local to this function
local var c1; //
Local to this function
private var c2; //
Local to this function
package var c3; //
Local to this function
public var c4; //
Local to this function
global var c5; //
Public global variable
package global var c6;//
Package-visible global variable
}
private function G() { //
Package-visible global function
var d0; //
Never defined because G
isn't called
global var d1; //
Never defined because G
isn't called
}
class C { //
Public global class
var e0; //
Public class instance variable
private var e1; //
Class-visible class instance variable
package var e2; //
Package-visible class instance variable
public var e3; //
Public class instance variable
static var e4; //
Public class-global variable
private static var e5;//
Class-visible class-global variable
package static var e6;//
Package-visible class-global variable
public static var e7; //
Public class-global variable
local var e8; //
Local to class C
's block
if (a2) {
var f0; //
Public class instance variable
private var f1; //
Class-visible class variable
package var f2; //
Package-visible class variable
public var f3; //
Public class variable
}
public function I() {}//
Public class method
}
F();
A static subset of JavaScript 2.0 would disallow definitions inside a function F that define names in a scope
outside F. This would disallow functions F
and G
above (as well as legacy programs that
define global variables from within functions by neglecting to use var
altogether).
Sometimes it is useful to add a namespace to a prior definition of a name (perhaps inherited from a superclass) or rename a name inherited from a superclass or imported package. The export definition provides a facility to make an alias of an existing name:
The first FunctionName is the new name. If the second FunctionName is provided then it is the existing name; otherwise, the first FunctionName is also the existing name. If one of the FunctionNames is a getter or setter, then the other one must be a getter or setter, respectively.
A new name is an exact alias of the old name rather than a separate value or entity. Thus, if method A is renamed as method B and B is subsequently overridden by B', then B' really overrides A.
Here are a few examples of definition renaming:
package Light { const red = 1; const green = 2; const blue = 4; const white = red + green + blue; } package Saber { use import Light; explicit namespace New; explicit namespace Improved; attribute New = namespace(New); attribute Imp = namespace(Improved); export white; // Re-export Light's white New export green; // Re-export Light's green, but only in the new version class C { package var size; function color() {} } interface I1 { function get length() {} } interface I2 { function get length() {} } class D extends C implements I1, I2 { var inertia; export size; // Make size public in instances of D export colour = color; // Make alias for British users function mispelled() {}; // Oops! Saber was already released and it's too late to remove this Imp export misspelled = mispelled; // Users of the improved version can use the good name export length = I2::length; // Resolve name conflict in favor of I2 } }
Should we have a protected
Attribute? It has been omitted
for now to keep the language simple, but there does not appear to be any fundamental reason why it could not be supported.
If we do support it, it might be better to choose the C++ protected
concept (visible only in class and subclasses);
the Java protected
concept (visible in class, subclasses, and the original class's package) could be represented
as a union of namespaces package
protected
.
Waldemar Horwat Last modified Tuesday, June 6, 2000 |