February 1999 Draft
JavaScript 2.0
Classes
|
Thursday, February 18, 1999
In JavaScript 2.0 we define classes using the class
keyword. Limited classes can also
be defined via JavaScript 1.x-style functions, but doing so is discouraged
for new code.
class
Identifier [extends
TypeExpression] Blockclass
extends
TypeExpression BlockThe first format declares a class with the name Identifier, binding Identifier
to this class in the scope specified by the Visibility
prefix (which usually includes the ClassDefinition's Block). Identifier
is a constant variable with type type
and can be used anywhere a type expression is allowed.
When the first ClassDefinition format is evaluated, the following steps take place:
Object
.const
,
var
, function
, constructor
, and class
declarations evaluated at its
top level (or placed at its top level by the scope rules) become class
members of type t. All field
and method
declarations evaluated at the Block's
top level (or placed at its top level by the scope rules) become instance
members of type t.A ClassDefinition's Block is evaluated just like any other Block, so it can contain expressions, statements, loops, etc. Such statements that do not contain declarations do not contribute members to the class being declared, but they are evaluated when the class is declared.
If a ClassDefinition omits the class name Identifier, it extends
the original class rather than creating a subclass. A class extension may define new methods and class constants and variables,
but it does not have special privileges in accessing the original class definition's private
members (or package
members if in a separate package). A class extension may not override methods, and it may not define constructors or instance
variables.
Each instance of the original class is automatically also an instance of the extended class. Several extensions can apply to the same class.
An extension is useful to add methods to system classes, as in the following code in some user package P
:
class extends string { public method scramble() string {...} public method unscramble() string {...} } var x = "abc".scramble();
Once the class extension is evaluated, methods scramble
and unscramble
become available on all
strings. There is no possibility of name clashes with extensions of class string
in other, unrelated packages
because the names scramble
and unscramble
belong to package P
and not the system package
that defines string
. Any packages that import package P
will also be able to call scramble
and unscramble
on strings, but other packages will not.
A class has an associated set of class members and another set of instance members. Class members are properties of the class itself, while instance members are properties of each instance object of this class and have independent values for different instance objects.
Class members are one of the following:
const
keyword.var
keyword.function
keyword.constructor
keyword.class
keyword.Instance members are one of the following:
field
keyword.method
keyword.Members can only be defined within the intersection of the lexical and dynamic extent of a ClassDefinition's Block. A few examples illustrate this rule.
The code
var bool extended = false; function callIt(x) {return x()} class C { extended = true; public function square(integer x) integer {return x*x} if (extended) { public function cube(integer x) integer {return x*x*x} } else { public function reciprocal(double x) double {return 1/x} } field string firstName, lastName; method name() string {return firstName + lastName} public function genMethod(boolean b) { if (b) { public field time = 0; } else { public field date = 0; } } genMethod(true); }
defines class C
with members square
(a class function), cube
(a class function),
firstName
(an instance variable), lastName
(an instance variable), name
(an instance
method), and genMethod
(a class function).
On the other hand, executing the following code after the above example would be illegal due to three different errors:
genMethod(false); // Field date declared outside of C's block's dynamic extent public field color; // Field declared outside a class's block function genField() { public field style; } class D { genField(); // Field style declared outside D's block's lexical extent }
While a ClassDefinition's Block is being evaluated, the already defined class members (other than constructors) are visible and usable by the code in that Block. Afterwards members can be accessed in one of several ways:
package
or omitted), or anywhere within the current package or any package that imports the appropriate
version of the current package (if a member's Visibility is public
) can access
class members by using the .
operator on the class.package
or omitted), or anywhere within the current package or any package that imports the appropriate
version of the current package (if a member's Visibility is public
) can access
instance members by using the .
operator on any of the class's instances.A subclass inherits all members except constructors from its superclass. Class variables have only one global value, not one value per subclass. A subclass may override visible methods, but it may not override or shadow any other visible members. On the other hand, imports and versioning can hide members' names from some or all users in importing packages, including subclasses in importing packages.
We have already seen the definition syntax for variables and constants, functions, and classes. Any of these defined at a ClassDefinition's Block's top level (or placed at its top level by the scope rules) become class members of the class.
Fields, methods, and constructor definitions have their own syntax described below. These definitions must be lexically enclosed by a ClassDefinition's Block.
field
[TypeExpression] Identifier [=
AssignmentExpression] ,
... ,
[TypeExpression] Identifier [=
AssignmentExpression] ;
A FieldDefinition is similar to a VariableDefinition except that it defines an instance variable of the lexically enclosing class. Each new instance of the class contains a new, independent set of instance variables initialized to the values given by the AssignmentExpressions in the FieldDefinition.
Identifier is the name of the instance variable and TypeExpression is its type. Identifier can be any non-reserved identifier. TypeExpression is evaluated at the time the variable definition is evaluated and should evaluate to a type t. The TypeExpressions and AssignmentExpressions are evaluated once, at the time the FieldDefinition is evaluated, rather than every time an instance of the class is constructed; their values are saved for use in constructors.
If omitted, TypeExpression defaults to type any
for the first Identifier
being declared and to the previous Identifier's TypeExpression value
for each subsequent Identifier (the previous Identifier's TypeExpression
is not evaluated twice; only its value is reused).
If provided, AssignmentExpression gives the instance variable's initial value v.
If not, undefined
is assumed; an error occurs if undefined
cannot be coerced
to type t. AssignmentExpression is evaluated just after the TypeExpression
is evaluated. The value v is then coerced to the variable's type t and stored in the instance variable.
Any values subsequently assigned to the instance variable are also coerced to type t at the time of each such assignment.
Multiple instance variables separated by commas can be defined in the same FieldDefinition.
A field cannot be overridden in a subclass.
getter
| setter
] [final
] [override
] method
Identifier (
Parameters )
[TypeExpression] Blockgetter
| setter
] [final
] [override
] method
Identifier (
Parameters )
[TypeExpression] ;
A MethodDefinition is similar to a FunctionDefinition except that it defines an instance method of the lexically enclosing class. Parameters, the result TypeExpression, and the body Block behave just like for function definitions, with the following differences:
this
that refers to the instance object of the method's class on
which the method was called..
operator produces a function (more specifically, a closure) that is already dispatched and has this
bound to the left operand of the .
operator.traditional
syntax
for methods. Optional parameters must be specified explicitly.We call a regular method by combining the .
operator with a function call. For example:
class C {
returns
field int x = 3;
method m() {return x}
method n(x) {return x+4}
}
var c = new C;
c.m(); //3
returns
c.n(7); //11
var funct f = c.m; //f
is a zero-argument function with this
bound to c
returns
f(); //3
returns
c.x = 8;
f(); //8
A class c may override a method m defined in its superclass s. To do this, c
should define a method m' with the same name as m and use the override
keyword in the definition of m'. Overriding a method without using the override
keyword or using the override
keyword when not overriding
a method results in a warning intended to catch misspelled method names.
The overriding method m' does not have to have the same number or type of parameters as the overridden method m. In fact, since parameter types can be arbitrary expressions and are evaluated only during a call, checking for parameter type compatibility when the overriding method m is declared would require solving the halting problem. Moreover, defining overriding methods that are more general than overridden methods is useful.
A method defined with the final
keyword cannot
be overridden (or further overridden) in subclasses.
If a MethodDefinition contains the keyword getter
or setter
,
then the defined method is a getter or a setter. These are analogous to getter
and setter functions in that they are invoked without listing the parentheses after the method name.
A getter or setter method cannot be overridden. We could relax this restriction, but then we'd also
have to allow overriding of fields by getters, setters, or other fields, and, as a corollary, allow fields to be declared
final
.
constructor
Identifier (
Parameters )
BlockA constructor is a class function that creates a new instance of the lexically enclosing class c. A constructor's
body Block is required to call one of c's superclass's constructors. Afterwards it
may access the instance object under construction via the this
local variable. A constructor should not return
a value with a return
statement; the newly created object is returned automatically.
A constructor can have any non-reserved name, in which case we would invoke it as though it were a class function. In addition,
a constructor's Identifier can have the special name new
, in which case we invoke
it using the new
prefix operator syntax as in JavaScript 1.x.
Waldemar Horwat Last modified Thursday, February 18, 1999 |