JavaScript 2.0: Evolving a Language for Evolving Systems

Waldemar Horwat [email protected] 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 . 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 [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 and - switch do while if, while, for, switch, and statements, and exceptions. JavaScript 1.5 throw/try/catch 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 eval 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 (true 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 vari- this 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 Point 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 they didn’t already exist (or overwrites them prototype f.__proto__, which points to the global proto- 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 : 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, making the package author’s job tractable. • Programs assembled from components (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 , , and Object Number variable x and a subclass B of A with an ad- ; each user-defined class (section 6) String 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 { and objects4. var y; } 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 is considered Variables in JavaScript 2.0 can be typed us- o to have type , so it admits every ing the syntax Object value. The following statements, which would be errors in a statically typed lan- 4 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 String, 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 var 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 5.1 Strict Mode than a variable. If b were a const instead 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 to be refer- b 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 allowed anywhere between two tokens. rations with a type are not hoisted, const 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- tion 7). class Point { 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- 8 } forceable. • 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 which means to use for a particular problem one places the attribute dynamic in front 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 and Packages all web browsers produced by the Brows- ersRUs company: 7.1 Packages package BitTracker { A JavaScript 2.0 package is a collection of class Data { top-level definitions declared inside a var author; var contents; package statement. An import statement ... refers to an existing package and makes the function save() { } 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 would not be possible if Netscape’s ob- function size() {...} 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 namespace 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. BitTracker can release the updated Each class has a predefined private 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 { must not have its meaning hijacked by public function f() anything class does. {return "fC"} C } • Suppose that N is private or internal instead. Class C ought not c = new C; to be able to override a private or method, which might lead If one calls c.f(), one would expect to, internal to an object security violation. and does in fact, get "fC", the most derived 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 { oriented programming — return the lowest const x; const y; (most derived) definition of o.n::p. Thus, const z; in the example above c.public::f() } will return regardless of the "fC" 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 count 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 static, 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