March 1999 Draft
JavaScript 2.0
Classes
previousupnext

Wednesday, March 24, 1999

Class Definitions

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.

ClassDefinition 
   [Visibilityclass Identifier [extends TypeExpressionBlock
|  [Visibilityclass extends TypeExpression Block

The 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:

  1. A new type t is created.
  2. If extends TypeExpression is given, TypeExpression is evaluated to obtain a type s, which must be another class. If extends TypeExpression is absent, type s defaults to the class Object.
  3. Type t is made a subtype of type s.
  4. Identifier is lexically bound in the scope given by Visibility; however, at this time Identifier does not have a legal type yet and any attempt to read or write it results in an error.
  5. Block is evaluated.
  6. If Block is evaluated successfully (without throwing out an exception), all 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.
  7. The value of Identifier becomes type t. From now on Identifier is a constant and its value cannot be altered.

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.

Class Extensions

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.

Members

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:

Instance members are one of the following:

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(number x) number {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
}

Visibility

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:

Inheritance

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.

Member Definitions

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.

MemberDefinition 
   FieldDefinition
|  MethodDefinition
|  ConstructorDefinition

Field Definitions

FieldDefinition 
   [Visibilityfield [TypeExpressionIdentifier [= AssignmentExpression, ... , [TypeExpressionIdentifier [= 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.

Method Definitions

MethodDefinition 
   [Visibility] [getter | setter] [final] [overridemethod Identifier ( Parameters ) [TypeExpressionBlock
|  [Visibility] [getter | setter] [final] [overridemethod 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:

We call a regular method by combining the . operator with a function call. For example:

class C {
  field integer x = 3;
  method m() {return x}
  method n(x) {return x+4}
}

var c = new C;
c.m();                 //
returns 3
c.n(7);                //
returns 11
var Function f = c.m;  //
f is a zero-argument function with this bound to c
f();                   //
returns 3
c.x = 8;
f();                   //
returns 8

Method Overriding

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 warning is not an error to allow subclass c to either define a method if it is not present in s or override it if it is present in s -- this situation can arise when s is imported from a different package and provides several versions.

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.

Getter and Setter Methods

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 Definitions

ConstructorDefinition 
   [Visibilityconstructor Identifier ( Parameters ) Block

A 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 (when and how?). 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 Wednesday, March 24, 1999
previousupnext