JavaScript 2.0: Evolving a Language for Evolving Systems

Page created by Milton Norris
 
CONTINUE READING
JavaScript 2.0:
            Evolving a Language for Evolving Systems
                                         Waldemar Horwat
                                        waldemar@acm.org

Abstract
JavaScript 2.0 is the next major revision of the JavaScript language. Also known as ECMAScript
Edition 4, it is being standardized by the ECMA organization. This paper summarizes the needs
that drove the revision in the language and then describes some of the major new features of the
language to meet those needs — support for API evolution, classes, packages, object protection,
dynamic types, and scoping. JavaScript is a very widely used language, and evolving it presented
many unique challenges as well as some opportunities. The emphasis is on the rationale, insights,
and constraints that led to the features rather than trying to describe the complete language.

                                                           Netscape browsers through an interface
1 Introduction                                             called LiveConnect.

                                                           JavaScript as a language has computational
1.1 Background                                             facilities only — there are no input/output
JavaScript [6][8] is a web scripting language              primitives defined within the language. In-
invented by Brendan Eich at Netscape. This                 stead, each embedding of JavaScript within
language first appeared in 1996 as                         a particular environment provides the means
JavaScript 1.0 in Navigator 2.0. Since then                to interact with that environment. Within a
the language has undergone numerous addi-                  web browser JavaScript is used in conjunc-
tions and revisions [6], and the most recent               tion with a set of common interfaces, in-
released version is JavaScript 1.5.                        cluding the Document Object Model [11],
                                                           which allow JavaScript programs to interact
JavaScript has been enormously successful                  with web pages and the user. These inter-
— it is more than an order of magnitude                    faces are described by separate standards
more widely used than all other web client                 and are not part of the JavaScript language
languages combined. More than 25% of web                   itself. This paper concentrates on the
pages use JavaScript.                                      JavaScript language rather than the inter-
                                                           faces.
JavaScript programs are distributed in
source form, often embedded inside web
page elements, thus making it easy to author               1.2 Standardization
them without any tools other than a text                   After Netscape released JavaScript in Navi-
editor. This also makes it easier to learn the             gator 2.0, Microsoft implemented the lan-
language by examining existing web pages.                  guage, calling it JScript, in Internet Ex-
                                                           plorer 3.0. Netscape, Microsoft, and a num-
There is a plethora of synonymous names
                                                           ber of other companies got together and
for JavaScript. JavaScript, JScript, and
                                                           formed the TC39 committee in the ECMA
ECMAScript are all the same language.
JavaScript was originally called LiveScript                standards organization [2] in order to agree
                                                           on a common definition of the language.
but was renamed to JavaScript just before it
                                                           The first ECMA standard [3], calling the
was released. JavaScript is not related to
Java, although the two language implemen-                  language ECMAScript, was adopted by the
                                                           ECMA general assembly in June 1997 as the
tations can communicate with each other in
                                                           ECMA-262 standard. The second edition of

JavaScript 2.0: Evolving a Language for Evolving Systems                                             1
this standard, ECMA-262 Edition 2 [4], con-                attributes and conditional compilation (Sec-
sisted mainly of editorial fixes gathered in               tion 8). Section 9 concludes.
the process of making the ECMAScript ISO
standard 16262. The third edition of the
ECMAScript standard [5] was adopted in                     2 JavaScript 1.5
December 1999 and added numerous new                       JavaScript 1.5 (ECMAScript Edition 3) is an
features, including regular expressions,
                                                           object-based scripting language with a syn-
nested functions and closures, array and ob-
                                                           tax similar to C and Java. Statements such as
ject literals, the switch and do-while                     i f , w h i l e , f o r , s w i t c h , and
statements, and exceptions. JavaScript 1.5
                                                           throw/try/c a t c h will be familiar to
fully implements ECMAScript Edition 3.
                                                           C/C++ or Java programmers. Functions,
I’ve been involved at Netscape with both the               declared using the function keyword, can
implementation and standardization of                      nest and form true closures. For example,
JavaScript since 1998. I wrote parts of the                given the definitions
ECMAScript Edition 3 standard and am cur-                      function square(x) {
rently the editor of the draft ECMAScript                         return x*x;
Edition 4 standard.                                            }

In Editions 1 and 2, the ECMA committee                        function add(a) {
standardized existing practice, as the lan-                       return function(b) {
guage had already been implemented by                                return a+b;
Netscape, and Microsoft closely mirrored                          }
that implementation. In Edition 3, the role of                 }
the committee shifted to become more active                evaluating the expressions below produces
in the definition of new language features                 the values listed after the fi symbols:
before they were implemented by the ven-                       square(5) fi 25
dors; without this approach, the vendors’                      var f = add(3);
implementations would have quickly di-                         var g = add(6);
verged. This role continues with Edition 4,                    f(1) fi 4;
and, as a result, the interesting language de-                 g(5) fi 11;
sign discussions take place mainly within
the ECMA TC39 (now TC39TG1) working                        A function without a return statement
group.                                                     returns the value undefined.
This paper presents the results of a few of                Like Lisp, JavaScript provides an e v a l
these discussions. Although many of the is-                function that takes a string and compiles and
sues have been settled, Edition 4 has not yet              evaluates it as a JavaScript program; this
been approved or even specified in every                   allows self-constructing and self-modifying
detail. It is still likely to change and should            code. For example:
definitely be considered a preliminary draft.                  eval("square(8)+3") fi 67
                                                               eval("square = f") fi The
1.3 Outline                                                    source code for function f
Section 2 gives a brief description of the                     square(2) fi 5
existing language JavaScript 1.5. Section 3
summarizes the motivation behind Java-                     2.1 Values and Variables
Script 2.0. Individual areas and decisions are
covered in subsequent sections: types (Sec-                The basic values of JavaScript 1.5 are num-
tion 4); scoping and syntax issues (Sec-                   bers (double-precision IEEE floating-point
tion 5); classes (Section 6); namespaces,                  values including +0.0, –0.0, +∞, –∞, and
versioning, and packages (Section 7); and                  NaN), booleans (t r u e and false), the

JavaScript 2.0: Evolving a Language for Evolving Systems                                              2
special values null and undefined, im-                          strange("Apple ", false) fi
mutable Unicode strings, and general ob-                        "Apple Hello"
jects, which include arrays, regular expres-                    strange(20, true) fi
sions, dates, functions, and user-defined ob-                   "40Hello"
jects. All values have unlimited lifetime and              The last example also shows that + is poly-
are deleted only via garbage collection,                   morphic — it adds numbers, concatenates
which is transparent to the programmer.                    strings, and, when given a string and a num-
                                                           ber, converts the number to a string and
Variables are not statically typed and can
                                                           concatenates it with the other string.
hold any value. Variables are introduced
using var declarations as in:
    var x;                                                 2.2 Objects
    var y = z+5;                                           JavaScript 1.5 does not have classes; in-
                                                           stead, general objects use a prototype
An uninitialized variable gets the value                   mechanism to mimic inheritance. Every ob-
undefined. Variable declarations are                       ject is a collection of name-value pairs
lexically scoped, but only at function                     called properties, as well as a few special,
boundaries — all declarations directly                     hidden properties. One of the hidden prop-
within a function apply to the entire func-                erties is a prototype link1 which points to
tion, even above the point of declaration.                 another object or null.
Local blocks do not form scopes. If a func-
tion accesses an undeclared variable, it is                When reading property p of object x using
assumed to be a global variable. For exam-                 the expression x.p, the object x is searched
ple, in the definitions                                    first for a property named p. If there is one,
     function init(a) {                                    its value is returned; if not, x’s prototype
        b = a;                                             (let’s call it y) is searched for a property
     }                                                     named p. If there isn’t one, y’s prototype is
                                                           searched next and so on. If no property at all
     function strange(s, t) {
                                                           is found, the result is the value
        a = s;
                                                           undefined.
        if (t) {
          var a;                                           When writing property p of object x using
          a = a+a;                                         the expression x.p = v, a property named p
        }                                                  is created in x if it’s not there already and
        return a+b;                                        then assigned the value v. x’s prototype is
     }                                                     not affected by the assignment. The new
function strange defines a local variable                  property p in x will then shadow any prop-
a. It doesn’t matter that the var statement is             erty with the same name in x’s prototype and
nested within the if statement — the var                   can only be removed using the expression
statement creates a at the beginning of the                delete x.p.
function regardless of the value of t.
                                                           A property can be read or written using an
At this point evaluating                                   indirect name with the syntax x[s], where s
    strange("Apple ", false)                               is an expression that evaluates to a string (or
signals an error because the global variable               a value that can be converted into a string)
b is not defined. However, the following                   representing a property name. If s contains
statements evaluate successfully because                   the string "blue", then the expression
init creates the global variable b:                        x[s] is equivalent to x.blue. An array is
    init("Hello") fi undefined                             1
                                                            For historical reasons in Netscape’s JavaScript this hidden
                                                           prototype link is accessible as the property named
                                                           __proto__, but this is not part of the ECMA standard.

JavaScript 2.0: Evolving a Language for Evolving Systems                                                             3
an object with properties named "0", "1",                  method can refer to the object on which it
"2", …, "576", etc.; not all of these need                 was invoked using the this variable:
be present, so arrays are naturally sparse.
                                                               function Radius() {
An object is created by using the new op-                        return Math.sqrt(
erator on any function call: new f(args).                          this.x*this.x +
An object with no properties is created be-                        this.y*this.y);
fore entering the function and is accessible                   }
from inside the function via the this vari-                The following statement attaches Radius
able.                                                      as a property named radius visible from
The function f itself is an object with several            any Point object via its prototype:
properties. In particular, f.prototype
                                                               Point.prototype.radius =
points to the prototype that will be used for                  Radius;
objects created via new f(args).2 An ex-
ample illustrates these concepts:                              a.radius() fi 5
    function Point(px, py) {
       this.x = px;                                        The situation becomes much more compli-
       this.y = py;                                        cated when trying to define a prototype-
    }                                                      based hierarchy more than one level deep.
                                                           There are many subtle issues [9], and it is
     a = new Point(3,4);                                   easy to define one with either too much or
     origin = new Point(0,0);                              too little sharing.
     a.x fi 3
     a["y"] fi 4                                           2.3 Permissiveness
                                                           JavaScript 1.5 is very permissive — strings,
The prototype can be altered dynamically:
                                                           numbers, and other values are freely coerced
   Point.prototype.color =
                                                           into one another; functions can be called
   "red";
                                                           with the wrong number of arguments; global
     a.color fi "red"                                      variable declarations can be omitted; and
     origin.color fi "red"                                 semicolons separating statements on differ-
                                                           ent lines may be omitted in unambiguous
The object a can shadow its prototype as                   situations. This permissiveness is a mixed
well as acquire extra properties:                          blessing — in some situations it makes it
    a.color = "blue";                                      easier to write programs, but in others it
    a.weight = "heavy";                                    makes it easier to suffer from hidden and
                                                           confusing errors.
     a.color fi "blue"
     a.weight fi "heavy"                                   For example, nothing in JavaScript distin-
     origin.color fi "red"                                 guishes among regular functions (square
     origin.weight fi undefined                            in the examples above), functions intended
                                                           as constructors (Point), and functions in-
Methods can be attached to objects or their                tended as methods (Radius). JavaScript
prototypes. A method is any function. The                  lets one call P o i n t defined above as a
                                                           function (without new and without attaching
                                                           it to an object),
2
  Using the notation from the previous footnote, after
                                                                p = Point(3)
o = new f(args),          o.__proto__ == f.prototype.      which creates global variables x and y if
f.prototype is not to be confused with function f’s own
prototype f.__proto__, which points to the global proto-
                                                           they didn’t already exist (or overwrites them
type of functions Function.prototype.                      if they did) and then writes 3 to x and

JavaScript 2.0: Evolving a Language for Evolving Systems                                              4
undefined to y. The variable p gets the                          3.2 Mechanisms
value undefined. Obvious, right? (If this
is obvious, then you’ve been spending far                        A package facility (separable libraries that
too much time reading language standards.)3                      export top-level definitions — see section 7)
                                                                 helps with some of the above requirements
                                                                 but, by itself, is not sufficient. Unlike exist-
2.4 Exploring Further                                            ing JavaScript programs which tend to be
This is only a brief overview of JavaScript                      monolithic, packages and their clients are
1.5. See a good reference [6] for the details.                   typically written by different people at dif-
To get an interactive JavaScript shell, type                     ferent times. This presents the problem of
javascript: as the URL in a Netscape                             the author or maintainer of a package not
browser or download and compile the source                       having access to all of its clients to test the
code for a simple stand-alone JavaScript                         package, or, conversely, the author of a cli-
shell from [8].                                                  ent not having access to all versions of the
                                                                 package to test against — even if the author
                                                                 of a client could test his client against all ex-
3 JavaScript 2.0 Motivation                                      isting versions of a package, he is not able to
                                                                 test against future versions. Merely adding
JavaScript 2.0 is Netscape’s implementation                      packages to a language without solving
of the ECMAScript Edition 4 standard cur-                        these problems would not achieve robust-
rently under development. The proposed                           ness; instead, additional facilities for defin-
standard is motivated by the need to achieve                     ing stronger boundaries between packages
better support for programming in the large                      and clients are needed.
as well as fix some of the existing problems
in JavaScript (section 5).                                       One approach that helps is to make the lan-
                                                                 guage more disciplined by adding optional
                                                                 types and type-checking (section 4). Another
3.1 Programming in the Large                                     is a coherent and disciplined syntax for de-
As used here, programming in the large does                      fining classes (section 6) together with a ro-
not mean writing large programs. Rather, it                      bust means for versioning of classes. Unlike
refers to:                                                       JavaScript 1.5, the author of a class can
 • Programs written by more than one per-                        guarantee invariants concerning its instances
    son                                                          and can control access to its instances,
 • Programs assembled from components                            making the package author’s job tractable.
    (packages)                                                   Versioning (section 7) and enforceable in-
                                                                 variants simplify the package author’s job of
 • Programs that live in heterogeneous en-
                                                                 evolving an already-published package, per-
    vironments
                                                                 haps expanding its exposed interface, with-
 • Programs that use or expose evolving                          out breaking existing clients. Conditional
    interfaces                                                   compilation (section 8) allows the author of
 • Long-lived programs that evolve over                          a client to craft a program that works in a
    time                                                         variety of environments, taking advantage of
Many applications on the web fall into one                       optional packages if they are provided and
or more of these categories.                                     using workarounds if not.

                                                                 To work in multi-language environments,
                                                                 JavaScript 2.0 provides better mappings for
                                                                 data types and interfaces commonly exposed
                                                                 by other languages. It includes support for
3
  The reason that global variables x and y got created is that   classes as well as previously missing basic
when one doesn’t specify a this value when calling a func-
tion such as Point, then this refers to the global scope         types such as long.
object; thus this.x = px creates the global variable x.

JavaScript 2.0: Evolving a Language for Evolving Systems                                                        5
3.3 Non-Goals                                                         var v:type = value;
                                                                  where v is the name of the variable and type
JavaScript 2.0 is intended for a specific
                                                                  is a constant expression that evaluates to a
niche of scripting languages. It is meant to
                                                                  type. Types can also be attached to function
be a glue language. It is not meant to be:
                                                                  parameters and results.
 • a high-performance language
 • a language for writing general-purpose                         A variable declared with a type is guaran-
    applications such as spreadsheets, word                       teed to always hold an element of that type5.
    processors, etc.                                              Assigning a value to that variable coerces
 • a language for writing huge programs                           the value to the type or generates an error if
                                                                  the coercion is not allowed. To catch errors,
 • a stripped-down version of an existing
                                                                  such coercions are less permissive than
    language
                                                                  JavaScript 1.5’s coercions.
Although many of the facilities provided
improve performance, that by itself is not                        4.2 Strong Dynamic Typing
their reason for inclusion in the language.
                                                                  JavaScript 2.0 is strongly typed — type
                                                                  declarations are enforced. On the other hand,
4 Type System                                                     JavaScript 2.0 is not statically typed — the
                                                                  compiler does not verify that type errors
JavaScript 2.0 supports the notion of a type,                     cannot occur at run time. To illustrate the
which can be thought of as a subset of all                        difference, consider the class definitions
possible values. There are some built-in                          below, which define a class A with instance
types such as O b j e c t , Number, and                           variable x and a subclass B of A with an ad-
String; each user-defined class (section 6)                       ditional instance variable y:
is also a type.
                                                                       class A {
The root of the type hierarchy is Object.                                var x;
Every value is a member of the type                                    }
Object. Unlike in JavaScript 1.5, there is
no real distinction between primitive values                           class B extends A {
                                                                         var y;
and objects4.
                                                                       }
Unlike in C and Java, types are first-class
values. Type expressions are merely value                         Given the above, the following statements
expressions that evaluate to values that are                      all work as expected:
types; therefore, type expressions use the                             var a:A = new A;
same syntax as value expressions.                                      var b:B = new B;
                                                                       a = b;
                                                                       var o = new A;
4.1 Type Declarations
                                                                  An untyped variable such as o is considered
Variables in JavaScript 2.0 can be typed us-
                                                                  to have type Object, so it admits every
ing the syntax
                                                                  value. The following statements, which
4
                                                                  would be errors in a statically typed lan-
 The bizarre JavaScript 1.5 dichotomy between        String,
                                                                  guage, also execute properly because the
Number, and Boolean values and String, Number, and
Boolean objects is eliminated, although an implementation         run-time values being assigned are of the
may preserve it as an optional language extension for com-        proper type:
patibility. All JavaScript 2.0 values behave as though they are
objects — they have methods and properties — although
some of the more important classes such as S t r i n g,           5
                                                                   Actually, the rule is that successfully reading a variable
Number, etc. are final and don’t allow the creation of            always returns an element of the variable’s type. This is
dynamic properties, so their instances can be transparently       because a variable may be in an uninitialized state, in which
implemented as primitives.                                        case trying to read it generates an error.

JavaScript 2.0: Evolving a Language for Evolving Systems                                                                     6
b = a;                                                        4.3 Rationale
     a = o;
                                                                   Why doesn’t JavaScript 2.0 support static
On the other hand, assigning b = o gen-                            typing? Although this would help catch pro-
erates a run-time error because o does not                         grammer errors, it would also dramatically
currently contain an instance of B.                                change the flavor of the language. Many of
                                                                   the familiar idioms would no longer work,
Because JavaScript is not statically typed,                        and the language would need to acquire the
function sum below also compiles correctly;                        concept of interfaces which would then have
it would be a compile-time error in a stati-                       to be used almost everywhere. Followed to
cally typed language because the compiler                          the logical conclusion, the language would
could not statically prove that c will have a                      become nearly indistinguishable from Java
property named y.6                                                 or C#; there is no need for another such lan-
    function sum(c:A) {                                            guage.
       return c.x + c.y;
    }                                                              Another common question is why
                                                                   JavaScript 2.0 uses the colon notation for
The assignment to z1 will execute success-                         type annotation instead of copying the C-
fully, while the assignment to z2 will gen-                        like syntax. Embarrassingly, this is a deci-
erates a run-time error when trying to look                        sion based purely on a historical standards
up c.y:7                                                           committee vote — this seemed like a good
                                                                   idea at one time. There is no technical rea-
    var z1 = sum(new B);
                                                                   son for using this syntax, but it’s too late to
    var z2 = sum(new A);
                                                                   reverse it now (implementations using this
The declaration c:A inside sum is still en-                        syntax have already shipped), even though
forced — it requires that the argument                             most of the people involved with it admit the
passed to sum must be a member of type A;                          syntax is a mistake.
thus, an attempt to call sum on an instance
of some class C unrelated to A would gener-                        5 Scoping and Strict Mode
ate an error even if that instance happened to
have properties x and y.                                           JavaScript 1.5 suffers from a number of de-
                                                                   sign mistakes (see sections 2.1 and 2.3 for
The general principle here is that only the                        some examples) that are causing problems in
actual run-time type of an expression’s value                      JavaScript 2.0. One of the problems is that
matters — unlike statically typed languages                        all var declarations inside a function are
such as C++ and Java, JavaScript 2.0 has no                        hoisted, which means that they take effect at
concept of the static type of an expression.                       the very beginning of the function even if
                                                                   the v a r declarations are nested inside
                                                                   blocks. Furthermore, duplicate var decla-
                                                                   rations are merged into one. This is fine for
                                                                   untyped variables, but what should happen
6
  If class A were final, a smart JavaScript compiler could         for typed variables? What should the inter-
issue a compile-time error for function sum because it could       pretation of the following function be?
prove that no possible value of c could have a property                 function f(a) {
named y. The difference here is that a compiler for a stati-
cally typed language will issue an error if it cannot prove that           if (a) {
the program will work without type errors. A compiler for a                   var b:String = g();
dynamically typed language will issue an error only if it can              } else {
prove that the program cannot work without type errors;
strong typing is ensured at run time.                                         var b:Number = 17;
7
  Unlike with prototype-based objects, by default an attempt               }
to refer to a nonexistent property of a class instance signals          }
an error instead of returning undefined or creating the
property. See section 6.

JavaScript 2.0: Evolving a Language for Evolving Systems                                                        7
Using JavaScript 1.5 rules would interpret                 variable. Moreover, if a block declares a lo-
the function as the following, which would                 cal variable named x, then an outer block in
be an error because now b has two different                the same function may not refer to a global
types:                                                     variable named x. Thus, the following code
    function f(a) {                                        is in error because the return statement is
       var b:String;                                       not permitted to refer to the global x:
       var b:Number;                                            var x = 3;
       if (a) {
          b = g();                                             function f(a) {
       } else {                                                  if (a) {
          b = 17;                                                  var x:Number = 5;
       }                                                         }
    }                                                            return x;
                                                               }
JavaScript 2.0 also introduces the notion of
const, which declares a constant rather
than a variable. If b were a const instead
                                                           5.1 Strict Mode
of a var, then even if the two declarations                Some of JavaScript 1.5’s quirks can’t be
had the same type then it would be undesir-                corrected without breaking compatibility.
able to hoist it:                                          For these JavaScript 2.0 introduces the no-
    function f(a) {                                        tion of a strict mode which turns off some of
        if (a) {                                           the more troublesome behavior. In addition
          const b = 5;                                     to making all declarations lexically scoped,
        } else {                                           strict mode does the following:
          const b = 17;
                                                            • Variables must be declared — mis-
        }
                                                               spelled variables no longer automati-
    }
                                                               cally create new global variables.
should not become:
                                                            • Function declarations are immutable
    function f(a) {                                            (JavaScript 1.5 treats any function dec-
        const b;                                               laration as declaring a variable that may
        if (a) {
                                                               be replaced by another function or any
          b = 5;
                                                               other value at any time).
        } else {
          b = 17;                                           • Function calls are checked to make sure
        }                                                      that they provide the proper number of
    }                                                          arguments. JavaScript 2.0 provides an
                                                               explicit way of declaring functions that
For one thing, the latter allows b to be refer-
                                                               take optional, named, or variable
enced after the end of the if statement.                       amounts of arguments.
To solve these problems while remaining                     • Semicolon insertion changes — line
compatible with JavaScript 1.5, Java-                          breaks are no longer significant in strict-
Script 2.0 adopts block scoping with one                       mode JavaScript 2.0 source code. Line
exception: var declarations without a type                     breaks no longer turn into semicolons
and in non-strict mode (see below) are still                   (as they do in some places in
hoisted to the top of a function. var decla-                   JavaScript 1.5), and they are now
rations with a type are not hoisted, const                     allowed anywhere between two tokens.
declarations are not hoisted, and declarations             Strict and non-strict parts may be mixed
in strict mode are not hoisted. To help catch              freely within a program. For compatibility,
errors, a block nested inside another block                the default is non-strict mode.
within a function may not redeclare a local

JavaScript 2.0: Evolving a Language for Evolving Systems                                                8
6 Classes                                                           enforce these rules without cooperation
                                                                    from its clients, which allows well-con-
In addition to the prototype-based objects of                       structed classes to rely on their invari-
JavaScript 1.5, JavaScript 2.0 supports class-                      ants regardless of what their clients do.
based objects. Class declarations are best                     •    Classes provide a good basis for
illustrated by an example:                                          versioning and access control (sec-
    class Point {                                                   tion 7).
      var x:Number;                                            •    Prototype-based languages naturally
      var y:Number;                                                 evolve classes anyway by convention,
                                                                    typically by introducing dual hierarchies
        function radius() {                                         that include prototype and traits objects
          return Math.sqrt(                                         [1]. Placing classes in the language
            x*x + y*y);                                             makes the convention uniform and en-
        }                                                           forceable.8
                                                               •    Complexity of prototypes. Few scrip-
        static var count = 0;                                       ters are sophisticated enough to cor-
    }                                                               rectly create a multi-level prototype-
                                                                    based hierarchy in JavaScript 1.5. In
A class definition is like a block in that it                       fact, this is difficult even for moderately
can contain arbitrary statements that are                           experienced programmers.
evaluated at the time execution reaches the
                                                               •   The class syntax is much more self-
class; however, definitions inside the class
                                                                    documenting than analogous Java-
define instance (or class if preceded with the
                                                                    Script 1.5 prototype hierarchies.
static attribute) members of the class in-
                                                               •    Classes as a primitive in the language
stead of local variables. Classes can inherit
                                                                    provide a valuable means of reflecting
from other classes, but multiple inheritance
                                                                    other languages’ data structures in
is not supported.
                                                                    JavaScript 2.0 and vice versa.
Classes can co-exist with prototype-based                      •    Classes are one of the most-requested
objects. The syntax to read or write a prop-                        features in JavaScript.
erty (object.property) is the same regardless
of whether the object is prototype or class-               Introducing two means of doing something
based. By default, accessing a nonexistent                 (classes and prototypes) always carries some
property of a class instance is an error, but if           burden of having to choose ahead of time
one places the attribute dynamic in front                  which means to use for a particular problem
                                                           and the subsequent danger of needing to re-
of the class declaration then one can create
                                                           cover from having made the wrong choice.
new dynamic properties on that class’s in-
                                                           However, it’s likely that at some point in the
stances just like for prototype-based objects.
                                                           future most programmers will use classes
                                                           exclusively and not even bother to learn
6.1 Rationale                                              prototypes. To make recovery easier, the
There are a number of reasons classes were                 syntax for routine usage of classes and pro-
added to JavaScript 2.0:                                   totypes is identical, so changing one to the
                                                           other only requires changing the declaration.
 • Classes provide stronger and more
   flexible abstractions than prototypes. A                8
                                                             An earlier JavaScript 2.0 proposal actually reflected a
   class can determine the pattern of mem-                 class’s members via prototypes and traits objects and allowed
   bers that each instance must have, con-                 any class instance to serve as a base for prototype inheritance
                                                           and vice versa. That proposal was dropped because it made
   trol the creation of instances, and control             language implementation much more complex than desired
   both its usage and overriding interfaces.               and required the authors of classes to think about not only
   Furthermore, a JavaScript 2.0 class can                 constructors but also cloners just in the rare case that a client
                                                           used the classes’ instances as prototypes.

JavaScript 2.0: Evolving a Language for Evolving Systems                                                                  9
To keep the language simple, there is no no-               7.3 Scenario
tion of Java-like interfaces. Unlike in Java,
these are not necessary for polymorphism                   Here’s an example of how such a collision
because JavaScript 2.0 is dynamically typed.               can arise. Suppose that a package provider
                                                           creates a package called BitTracker that
                                                           exports a class Data. This package be-
7 Namespaces, Versioning,                                  comes so successful that it is bundled with
                                                           all web browsers produced by the Brows-
and Packages                                               ersRUs company:
                                                               package BitTracker {
7.1 Packages
                                                              class Data {
A JavaScript 2.0 package is a collection of
                                                                var author;
top-level definitions declared inside a
                                                                var contents;
package statement. An import statement
                                                                function save() {...}
refers to an existing package and makes the
                                                              }
top-level definitions from that package
available. The exact scheme used to name                      function store(d) {
and locate existing packages is necessarily                     ...
dependent on the environment in which                           storeOnFastDisk(d);
JavaScript 2.0 is embedded and will be de-                    }
fined and standardized independently as                       }
needed for each kind of embedding (brows-
ers, servers, standalone implementations,                  Now someone else writes a client web page
etc.).                                                     W that takes advantage of BitTracker.
                                                           The class Picture derives from Data and
7.2 Versioning Issues                                      adds, among other things, a method called
                                                           size that returns the dimensions of the
As a package evolves over time it often be-                picture:
comes necessary to change its exported in-                     import BitTracker;
terface. Most of these changes involve add-
ing definitions (top-level or class members),                 class Picture extends
although occasionally a definition may be                            Data {
deleted or renamed. In a monolithic envi-                        function size() {...}
ronment where all JavaScript source code                         var palette;
comes preassembled from the same source,                      };
this is not a problem. On the other hand, if
packages are dynamically linked from sev-                     function orientation(d) {
eral sources then versioning problems are                       if (d.size().h >=
likely to arise.                                                     d.size().v)
                                                                  return "Landscape";
One of the most common avoidable prob-                          else
lems is collision of definitions. Unless this                     return "Portrait";
problem is solved, an author of a package                     }
will not be able to add even one definition in
a future version of his package because that               The author of the BitTracker package,
definition’s name could already be in use by               who hasn’t seen W, decides in response to
some client or some other package that a                   customer requests to add a method called
client also links with. This problem occurs                size that returns the number of bytes of
both in the global scope and in the scopes of              data in a Data object. He then releases the
classes from which clients are allowed to                  new and improved BitTracker package.
inherit.

JavaScript 2.0: Evolving a Language for Evolving Systems                                           10
BrowsersRUs includes this package with its                    originates. Unfortunately, this would get
latest Navigator 17.0 browser:                                tedious and unnecessarily impact casual
                                                              uses of the language. Furthermore, this
    package BitTracker {                                      approach is impractical for names of
    class Data {                                              methods because it is often desirable to
      var author;                                             share the same method name across sev-
      var contents;                                           eral classes to attain polymorphism; this
      function size() {...}                                   would not be possible if Netscape’s ob-
                                                              jects used com_netscape_length
      function save() {...}
    }                                                         while MIT’s objects all used
                                                              edu_mit_length.
    function store(d) {                                     • Explicit imports. Each client package
      ...                                                     could be required to import every exter-
      if (d.size() > limit)                                   nal name it references. This works rea-
          storeOnSlowDisk(d);                                 sonably well for global names but be-
      else                                                    comes tedious for the names of class
          storeOnFastDisk(d);                                 members, which would have to be im-
    }                                                         ported separately for each class.
    }
                                                            • Resolve names at compile time. Java
An unsuspecting user U upgrades his old                       and C# resolve the references of names
BrowsersRUs browser to the latest Naviga-                     to the equivalents of vtable slots at com-
tor 17.0 browser and a week later is dis-                     pile time. This way size inside store
mayed to find that page W doesn’t work                        can resolve to something other than
anymore. U’s grandson tries to explain to U                   size inside orientation. Unfortu-
that he’s experiencing a name conflict on the                 nately, this approach works only for
size methods, but U has no idea what the                      statically typed languages. Moreover,
kid is talking about. U attempts to contact                   this approach relies on object code
the author of W, but she has moved on to                      rather than source code being distributed
other pursuits and is on a self-discovery                     — the ambiguity is still present in the
mission to sub-Saharan Africa. Now U is                       source code, and it is only the extra data
steaming at BrowsersRUs, which in turn is                     inserted by past compilation of the client
pointing its collective finger at the author of               against an older version of the package
BitTracker.                                                   that resolves it.
                                                            • Versions. Package authors could mark
Note that this name collision occurs inside a                 the names they export with explicit ver-
class and is much more insidious than                         sions. A package’s developer could in-
merely a conflict among global declarations                   troduce a new version of the package
from imported packages.                                       with additional names as long as those
                                                              names were made invisible to clients
7.4 Solutions                                                 expecting to link with prior versions.

How could the author of BitTracker                         JavaScript 2.0 follows the last approach. It is
have avoided this problem? Simply choos-                   the most desirable because it places the
ing a name other than size wouldn’t work,                  smallest burden on casual users of the lan-
because there could be some other page W2                  guage, who merely have to import the pack-
that conflicts with the new name. There are                ages they use and supply the current version
several possible approaches:                               numbers in the import statements. A pack-
                                                           age author has to be careful not to disturb
  • Naming conventions. Each defined
                                                           the set of visible prior-version definitions
    name could be prefixed by the full name
    of the party from which this definition                when releasing an updated package, but

JavaScript 2.0: Evolving a Language for Evolving Systems                                               11
authors of dynamically linkable packages                        package BitTracker {
tend to be much more sophisticated than
casual users of the language.                                   explicit namespace v2;
                                                                use namespace(v2);
7.5 Namespaces                                                  class Data {
JavaScript 2.0 employs namespaces to pro-                         var author;
vide safe versioning. A package can define                        var contents;
and export several namespaces, each of                            v2 function size() {...}
which provides a different view of the pack-                      function save() {...}
age’s contents. Each namespace corresponds                      }
to a version of the package’s API.
                                                                function store(d) {
A JavaScript 2.0 namespace is a first-class                       ...
value that is merely a unique token and has                       if (d.size() > limit)
no members, internal structure, or inheri-                            storeOnSlowDisk(d);
tance. JavaScript namespaces are not related                      else
to C++ namespaces. On the other hand, the                             storeOnFastDisk(d);
designers of other dynamic languages such                       }
as Smalltalk have independently run into the                    }
same versioning problem and come up with
a solution similar to JavaScript 2.0’s (for                If the client W isn’t updated, then it will not
example, “Selector Namespaces” in [10]).                   be aware that BitTracker’s size exists
                                                           and will be able to refer to its own size
Each JavaScript 2.0 name is actually an or-                method. If the author of W later wants to
dered pair namespace::identifier, where                    revise her web page to also refer to
n a m e s p a c e is a simple expression that              BitTracker’s size, then she can either
evaluates to a namespace value. When a                     explicitly refer to v2::size or rename her
name is defined without a namespace, the
                                                           size to some other name and then import
namespace defaults to the predefined name-
                                                           v2.9
space public.

A use namespace(n) statement allows                        7.6 Private and Internal
unqualified access within a scope to identifi-
ers qualified with namespace n. There is an                In addition to providing access control
implicit use namespace(public)                             among packages, namespaces also work
statement around the whole program. For                    well within a package. In fact, early in the
convenience, a namespace may also be                       development of JavaScript 2.0 it became
specified when importing a package.                        apparent that the notions of private and
                                                           internal (visible inside a package only)
Given namespaces, the author of                            are simply special cases of namespaces.
B i t T r a c k e r can release the updated                Each class has a predefined p r i v a t e
package while hiding the size method                       namespace that can be referenced using the
from existing clients that don’t expect to see             private keyword inside that class and that
it:
                                                           9
                                                             This description is glossing over how the author of
                                                           BitTracker keeps the name of the namespace itself v2
                                                           from colliding with some other global definition inside the
                                                           client W. The basic explanation is that the explicit attrib-
                                                           ute keeps v2 itself from being imported by default. If a client
                                                           knows that v2 is there and won’t cause a conflict, then it can
                                                           explicitly request v2 to be imported; there is a convenient
                                                           syntax for doing that using an import statement. See [7] for
                                                           the details.

JavaScript 2.0: Evolving a Language for Evolving Systems                                                              12
is use’d only inside that class. Different                     class A {
classes have independent private name-                           N function f()
spaces.                                                            {return "fA"}
                                                               }
Namespaces offer a finer granularity of
permissions — a class can have some                            class B extends A {
private members visible only to itself,                          N function f()
but it can also define members defined in a                        {return "fB"}
custom namespace visible to some but not                       }
all of its users.                                              class C extends B {
                                                                 public function f()
7.7 Property Lookup                                                {return "fC"}
                                                               }
Namespaces control the behavior of looking
up either a qualified property n::p or an                      c = new C;
unqualified property p of an object o. It may
appear that there are many ways of defining                Now, if one calls c.f(), the result ought to
this lookup process, but in fact only one way              depend          on       whether           a
allows for reliable versioning. The resulting              use namespace(N) is lexically in effect
lookup process appears counterintuitive at                 around the expression c.f(). If it is not,
first but is in fact the correct one and de-               then the behavior is simple — only the f
serves a closer look.                                      defined by C is visible, so once again the
                                                           result is "fC".
The process is illustrated by a few examples.
Consider first the simple case of a hierarchy               If use namespace(N) is lexically in
of three classes:                                          effect around the expression c.f(), then
     class A {                                             all three definitions of f are visible. It would
        public function f()                                be tempting to choose the most derived one
           {return "fA"}                                   ("fC"), but this would be incorrect because:
     }
                                                             • This is analogous to the BitTracker
    class B extends A {                                         scenario. Class C might have defined
      public function f()                                       function f first and classes A and B later
        {return "fB"}                                           evolved to define their own, hidden f.
    }                                                           To make this work, any code in the lexi-
                                                                cal scope of a use namespace(N)
    class C extends B {
      public function f()                                       must not have its meaning hijacked by
        {return "fC"}                                           anything class C does.
    }                                                        • Suppose that N is p r i v a t e or
                                                                internal instead. Class C ought not
    c = new C;                                                  to be able to override a private or
If one calls c.f(), one would expect to,                        internal method, which might lead
and does in fact, get "fC", the most derived                    to an object security violation.
definition of f. This is the essence of object-            Other solutions such as signaling an ambi-
oriented semantics.                                        guity error when encountering the expres-
                                                           sion c.f() or alternately even preventing
Now let’s alter the example by putting A and
                                                           class C from being defined are also incorrect
B’s definitions of f into a namespace N:
                                                           for the same reasons as above.

JavaScript 2.0: Evolving a Language for Evolving Systems                                                13
The only sensible thing to do is to define the             several qualified names, one for each name-
language so that the expression c.f() in                   space, which is valuable for many
this case returns "fB". The rule for looking               versioning scenarios.
up an unqualified property p of an object o
                                                           Attributes may be placed before the opening
is therefore:
                                                           braces of a block, which distributes the at-
  • First, find the highest (least derived)                tributes among all the definitions inside that
     class that defines a property of o named
                                                           block. Thus, instead of
     p that’s visible in the currently used
                                                               class A {
     namespaces; let n be that member’s                            static private const x;
     namespace (if there is more than one                          static private const y;
     such namespace in the same class, just                        static private const z;
     pick one).                                                    function f() {}
  • Second, find and return the lowest (most                   }
     derived) definition of o.n::p.
                                                           one can write:
The rule for looking up a qualified property                   class A {
n::p of an object o is the same as in object-                      static private {
                                                                     const x;
oriented programming — return the lowest
                                                                     const y;
(most derived) definition of o.n::p. Thus,
                                                                     const z;
in the example above c.public::f()
                                                                   }
will return " f C " regardless of the                              function f() {}
use namespace declarations in effect.                          }
Perhaps not coincidentally, defining the                   Two other very useful attributes are true
rules this way makes JavaScript 2.0 much                   and false. The attribute true is ignored.
easier to compile by allowing partial                      false causes the entire definition or state-
evaluation of property lookups in many                     ment to disappear without being evaluated
common cases.                                              or processed further. By defining a global
                                                           boolean constant (or obtaining one from the
                                                           environment) one can achieve convenient
8 Attributes and                                           conditional compilation without resorting to
Conditional Compilation                                    a separate preprocessor language. In the ex-
                                                           ample below, instances of class A have the
Several of the previous sections implicitly                slot c o u n t and method g only if the
referenced attributes. This section explores               const definition of debug is changed to
them in a little more detail and uncovers an               true:
interesting and perhaps unexpected use of
                                                               const debug = false;
attributes for conditional compilation.
                                                               class A {
In JavaScript 2.0, attributes are simple con-                    var x;
stant expressions that can be listed in front                    debug var count;
of most definitions as well as a few other
statements. Examples of attributes already                         function f() {}
mentioned include s t a t i c , public,                            debug function g() {}
private, explicit, dynamic, final,                             }
as well as namespaces. These are, in fact,
constant expressions, and other attributes
may be defined using const definitions.                    9 Conclusion
Multiple attributes may be listed on the
same definition. Attaching multiple name-                  This paper presents only the highlights and
spaces to a definition simultaneously defines              some of the rationale of JavaScript 2.0.

JavaScript 2.0: Evolving a Language for Evolving Systems                                              14
There are a few other features, such as units,             hoc and especially complicated. Trying to
typed arrays, and operator overriding that                 avoid complexity while retaining compati-
were not covered here. See [7] for a much                  bility has been a constant struggle.
more detailed description.
                                                           Netscape’s implementations [8] of
It’s been a long road to get to this point,                JavaScript 1.5 and the forthcoming 2.0 are
with various proposals being discussed in                  available as open source under the NPL,
the ECMA TC39 working group for several                    GPL, or LGPL license. The JavaScript
years. Most of what made the problem so                    source code is compact and stand-alone and
difficult and time-consuming is the difficult              does not depend on the rest of the browser to
task of retaining backward compatibility.                  be compiled; it’s been embedded in
JavaScript 1.5 has grown many ad-hoc fea-                  hundreds if not thousands of different
tures that don’t carry forward well and tend               environments to date.
to make any future evolution even more ad-

Bibliography
  [1] Martin Abadi, Luca Cardelli. A Theory of Objects. Springer-Verlag 1996.
  [2] ECMA web site, http://www.ecma.ch/

  [3] ECMA-262 Edition 1 standard,
      http://www.mozilla.org/js/language/E262.pdf

  [4] ECMA-262 Edition 2 standard,
      http://www.mozilla.org/js/language/E262-2.pdf

  [5] ECMA-262 Edition 3 standard,
      http://www.mozilla.org/js/language/E262-3.pdf

  [6] David Flanagan. JavaScript: The Definitive Guide, 4th Edition. O’Reilly 2002.

  [7] Mozilla’s JavaScript 2.0 web site,
      http://www.mozilla.org/js/language/js20/

  [8] Mozilla’s JavaScript web site, http://www.mozilla.org/js/

  [9] Netscape. Object Hierarchy and Inheritance in JavaScript. Thunder Lizard Productions
      JavaScript Conference ’98, also available at
      http://developer.netscape.com/docs/manuals/communicator/jsobj
      /index.htm

[10] SmallScript LLC. SmallScript Website, http://www.smallscript.net/

[11] W3C Document Object Model, http://www.w3.org/DOM/

JavaScript 2.0: Evolving a Language for Evolving Systems                                             15
You can also read