If you’ve been keeping track of this blog, you might have noticed that I am a big fan of JavaScript. Although you can argue about the fundamental features that a language has, there are several reasons why I
consider JavaScript to be one of the hottest languages out there today.
A couple of days ago the organization responsible for the new ECMAScript, version 4 (AKA JavaScript 2) have published a revised version of the language overview. Although this is only a proposal, TG1 is no longer accepting more proposals and has started writing the new specs, so this “overview” is pretty much final.
It is no secret that the new version of JavaScript will be heavily influenced by…. no, not Java 🙂 The new JavaScript will be… Pythonic!
Following you can find some of the coolest features of the next big language.
Note: Some of the following features are already part of current JavaScript specs and are also implemented in Firefox 2 (eg. Generators and Iterators).
Type Annotations
Generally speaking types describe run-time values. In the new JavaScript a type annotation can (optionally) be added to a property, to a function parameter, to a function itself (applying to the function’s return value), to a variable, or to an object or array initializer.
Examples:
var x : String; // declares a string
function f(a : int, b : String, c : Object) : RetType {
// arguments are guaranteed to match types
// return value must convert to or match RetType
}
Classes
Class-based programming is a ubiquitous methodology in the industry. Object-oriented programming as we have known to love it in Java and other languages is supported in ES4 by classes and interfaces. A class describes an object by presenting those properties (fields) of the object that are always present (the fixed properties or fixtures), including variables, constants, and methods. In the JavaScript a class basically defines a named record type derived from another class, or from Object if no supeclass is given, and is immutable.
Example:
class C extends B {
function C(m) { mem = m }
public function getmem() { return mem }
private var mem : Object
}var c : C = new C(new Date)
Interfaces
An interface describes a collection of method properties; a class can declare that it implements an interface and then back that up by providing definitions for the
methods in the interface. Then instances of the class also have the type of the interface. In JavaScript an interface is a named record type, possibly derived from another interface, containing only function declarations with no body. A class may implement one or more interfaces
interface I { function f() }
interface J { function g(n) }
class C implements I, J {
function f() { return "foo" }
function g(n) { return n+2 }
}
Structural Types
Besides classes and interfaces, types can also be renamed or abbreviated by means of special type definitions, which are helpful in giving names to structural types:
type Q = {p: int, q: String}
type R = {p: int, q: String, r: Boolean}
type A = [int, String, Boolean, Object]
type F = function (int, String, Boolean): Object
type G = function (int, Object, Object): Number
Namespaces
Besides the built-ins (public, private, internal, intrinsic) you can declare your own with the namespace directive:
namespace ns = "synodinos.wordpress.com/"class C {
public function frob(): int {...}
public ns function frob(alpha: double): double { ... }
}
Packages
A package is a value comprised of a package name and two namespaces known as the package-internal and the package-public namespaces. The two namespaces are created by the ECMAScript implementation and are not visible to the program, but the package introduces a block scope in which these two namespaces are both open. The definitions in the package are evaluated in this block scope.
Examples:
//Wrap up related code in a first-class way
package org.mozilla.venkman {
class Debugger { . . . }
class Inspector { . . . }
class StackWalker { . . . }
}//Import names from a package…
import org.mozilla.venkman.*
//…or import selectively:
import org.mozilla.venkman.Inspector
let variables
The let directive introduces bindings into the innermost block object. It is considered as a “better var” because var only introduces bindings into the variable object.
Example:
function f(v1: Vec3, v2: Vec3) {
if (cross_product_only) {
let a1 = v1[0], a2 = v1[1], a3 = v1[2]
let b1 = v2[0], b2 = v2[1], b3 = v2[2]
return new Vec3(a2*b3 - a3*b2, a3*b1 - a1*b3, a1*b2 - a2*b1)
}
. . .
}
let variables have block scope and in combination with for loops contain an implicit block:
for (let i = 0; i < n; i++) sum += a[i]
for (let key in obj) print(key, obj[key])
Here is an example of a block statements using let:
//JS1 idiom:
(function (a, b) {
var x = b, y = a
var z = eval(s)
commit(x, y, z)
})(x, y)//With let much better
let (x = y, y = x, z) {
z = eval(s)
commit(x, y, z)
}
Example of Block Expressions using let:
//In the JS1 idiom:
return (function (a, b) {
var x = b, y = a
return commit(x, y, eval(s))
})(x, y)
//With let much much better!
return let (x = y, y = x, z) commit(x, y, eval(s))
Destructuring Assignment
Here are some simple cases of destructuring assignment and bindings:
//Group assignment and return:
// swap, rotate, permute in general
[a, b] = [b, a]
[p, q, r] = [q, r, p]// destructure a triple into fresh vars
var [s, v, o] = db.getTriple()
//Objects:
let {'Title': title, 'Height': height, 'Width': width} = jsonResult//for-in loops:
for (let [key, {'Title':title, 'FileSize':size}] in dict)
print(key, title, size)
Iterators
The iterators in the new JavaScript are similar to those in Python:
Example:
let it = Iterator(["apple", "orange", "banana"])it.next() //returns [0, "apple"]
it.next() //returns [1, "orange"]
it.next() //returns [2, "banana"]
it.next() //throws StopIteration//The for-in loop handles StopIteration:
for (let i in it) print(i)
Generators
When developing code that involves an iterative algorithm (such as iterating over a list, or repeatedly performing computations on the same data set), there are often state variables whose values need to be maintained for the duration of the computation process. Traditionally, you have to use a callback function to obtain the intermediate values of an iterative algorithm. Generators and iterators work together to provide a new, better, way to do this.
A function containing the new keyword yield is a generator: when it is called, it binds its parameters to their values and immediately returns a new generator-iterator object which can be called repeatedly to yield new values:
function count(n : int) {
for (let i = 0; i < n; i++)
yield i
}var gen = count(10)
gen.next() //returns 0; . . .; gen.next() returns 9
gen.next() //throws StopIteration
gen.send(value) //passes value back to yield
gen.throw(exception) //throws exception from yield
Array Comprehensions
Array comprehensions are a use of generators that provides a convenient way to perform powerful initialization of arrays:
let squares = [i * i for (i in count(10))]
print(squares) => "0,1,4,9,...,81"
let odd_squares = [i * i for (i in count(10)) if (i % 2)]
return [i * j for (i in it1) for (j in it2) if (i != j)]
Decimal pragma and type
A pragma is a directive that appears at the beginning of a block and its effect is limited to that block only. For doing math you can use the use decimal pragma, which selects a DecimalContext object containing rounding and precision
settings for decimal arithmetic.
//In JS1…
print(79.49 - 35.99) // => 43.4999999999999
//… but know
{
use decimal
print(79.49 - 35.99) // => 43.50
}
In ECMAScript 3 the type double represents a 64-bit IEEE 754 binary
floating-point number. This is needed for backwards compatibility and is suitable for most computations since it takes advantage of the floating-point hardware on current CPUs. In the new version decimal represent a 128-bit IEEE 754r decimal floating-point number and provides base-10 arithmetic to high precision (34 decimal digits) which is is not yet widely implemented in hardware.