overview

summary of proposed ECMAScript 4.0 features not already in ActionScript 3.0

language still a work in progress (this lecture last updated april 17 2008)

ECMAScript 4 specification release date: December 2008

resources...

  • Proposals: http://wiki.ecmascript.org/doku.php?id=proposals:proposals
  • Overview: http://www.ecmascript.org/es4/spec/overview.pdf
  • Feature discussion: http://bugs.ecmascript.org/
  • Mailing list: https://mail.mozilla.org/listinfo/es4-discuss
  • Francis Cheng's blog: http://blogs.adobe.com/fcheng/
  • structural types

    record type

    array type

    record type

    record type: describes the structure of a set of objects

    type Point = { x: int, y: int }

    any object with fixed int x and int y belongs to Point

  • easier to create than a class
  • more flexible than a class (at least x and y, but may have other members)
  • type safety for lightweight objects
  • record type examples

    new record type instance, with args for x and y

    new Point( 3, 10 )

    new record type instance, type set with type annotation

    { x:3, y:10 } : Point

    inline usage:

    public function moveTo (p:{ x: int, y: int }) { x = p.x; y = p.y; } moveTo(somePoint); moveTo(someSprite); // Failure because untyped object doesn't have required type. moveTo({x:10, y:20}); // Success because object's type is specified. moveTo({x:10, y:20}:{ x: int, y: int });

    array type

    array type: type that describes the structure of an array

    type intArray = [int]; // All elements must be int

    type myArray = [int,string]; // First element must be int, // second must be string, // remaining elements unconstrained

    new array type instance, with 7 elements:

    new intArray(7)

    new array type instance, type set with type annotation

    [ 1,2,3 ] : intArray

    nested structural types

    type Person = { name:{ last:string, first:string }, born:Date, spouse:* }

    [[int]] // Array of arrays whose elements are type int

    union types

    // x can be assigned either an int or a string var x:(int|string)

    public function send (msg:(string|XML)):void {...}

    Boolean, String, Number replace ES3 primitives

    CURRENT BUILT-IN STATUS IS IN FLUX

    ECMAScript 4 built-ins:

  • boolean, string, byte, uint, int, double, decimal
  • wrapper classes for boolean, string, double:

  • Boolean, String, Number
  • wrappers are dynamic, non-final, nullable

    wrappers behave like ES3's primitives of the same name

    predefined union types

    new union types:

    type AnyString = (string,String) type AnyBoolean = (boolean,Boolean) type AnyNumber = (byte,int,uint,double,decimal,Number) type FloatNumber = (double,decimal)

    handy for writing libraries used by legacy and new code

    type constraints on rest arguments

    "rest" arguments can have type constraint

    function drawPolygon (p1:Point, p2:Point, p3:Point, ...[Point]): void

    non-nullable types

    // "!" indicates that v cannot be assigned null var v : C!

    // "?" indicates that v can be assigned null var v : C?

    // vars of type C cannot be assigned null class C! { ... }

    rationale: earlier (compile-time) detection of null-pointer errors

    instance initialization

    assign constructor parameter directly to an instance variable

    // Assign x the value of parameter p class C { var x : D! // must be initialized function C (p):x = p { ... } }

    the like operator

    like tests whether an object is structurally similar to a given type

    // v will accept any of these: // { x:3, y:3 } : Point // { x:3, y:3 } // new MovieClip() var v: like { x: int, y: int }

    use case: old ECMAScript 3 code using upgraded ECMAScript 4 library

    the wrap operator

    THIS FEATURE HAS BEEN RETIRED

    wrap can give an untyped object a type

    o has no type:

    var o:* = { x: 10, y: 20 };

    give o type Point:

    type Point { x: int, y: int }; var v: wrap Point = o;

    legally assign o (now with type) to w (whose type is Point):

    var w:Point = v;

    parameterized types

    datatype used in class code dynamically determined on a per-instance basis

    similar to C#'s "generics"

    used primarily to implement core-language-level collections

    class Hashtable.<K,V> { var store : [ [K,V] ]; function fetch(k:K) : V { var ix : uint = hash_func(k); while (store[ix][0] != k) ix = ... // some probe sequence return store[ix][1]; } } // Make a new Hashtable, with uint keys and string vals var h : Hashtable.<uint,string> = new Hashtable.<uint,string>();

    lexical block scoping

    use 'let' instead of 'var' to create block-scoped vars

    { let a = 10, b=1 { let a = 20 trace(a+b) // 21 } trace(a) // 10 }

    expression closures

    single-line functions

    function getWidth () this.width;

    &&= and ||=

    &&= not often used

    ||= used for assigning default values

    // this code... myVar = myVar || "default"; // can be shortened to: myVar ||= "default";

    generic functions ("multimethods")

    THIS FEATURE HAS BEEN DEFERRED

    java-style method overloading

    create generic function first:

    generic function intersect(s1, s2);

    attach ("overloaded") methods to the generic function:

    generic function intersect(s1: Shape, s2: Shape) { // general intersection method } generic function intersect(s1: Rect, s2: Rect) { // fast intersection for rectangles }

    not currently available in instance-method form

    program units

    THIS FEATURE HAS BEEN DEFERRED

    similar to #include, but based on semantics, not files

    unit URLParser { use unit Splitter "http://www.mycompany.com/lib/splitter" use unit Unicode "http://www.mycompany.com/lib/Unicode" package com.mycompany.urlparser { ... } } use unit URLParser "http://www.mycompany.com/lib/URLParser"

    iterators

    based on python's iteration protocol

    an interable object must have the following structural type:

    type IterableType.<T> = { iterator::get: function (boolean=) : IteratorType.<T> };

    an iterator object must have the following structural type:

    type IteratorType.<T> = { next: function () : T };

    for (i in o) ... updated to perform iteration equivalent to:

    let ($it = o.iterator::get(true)) { while (true) { try { i = $it.next(); } catch (e : iterator::StopIterationClass) { break; } ... } }

    example showing iteration over Fibonacci sequence:

    // The iterable and iterator class: class Fibber { private var a=0, b=1 private var cutoff:int = int.MAX_VALUE; public Fibber (cutoff:int) { this.cutoff = cutoff; } iterator function get(deep:boolean = false): IteratorType.<int> this public function next(): int { if (b >= cutoff) throw iterator::StopIteration [a,b] = [b,a+b] return a } } // Usage: for ( i in new Fibber(1000) ) trace(i)

    generators

    generator: function that generates a sequence of values for iteration

    when called, the generator returns an iterator, but does not run

    calling iterator.next() does 3 things:

  • runs code in the function up to yield
  • returns the yield value
  • suspends function execution
  • // A generator that yields 3 values function countdown() { yield 3; yield 2; yield 1; } var counter = countdown(); trace(counter.next()); // 3 trace(counter.next()); // 2 trace(counter.next()); // 1 // None left, so iterator::StopIteration exception is thrown trace(counter.next()); // ERROR!

    python inspired

    generators: rationale

    rationale: elegant way to traverse data structures (esp. trees)

    // A generator for finding leaves in a tree structure function fringe (tree) { if (tree is like {left:*, right:*}) { for (let leaf in fringe(tree.left)) yield leaf for (let leaf in fringe(tree.right)) yield leaf } else { yield tree } }

    // Make a sample tree var tree = { left: { left: 37, right: 42 }, right: "foo" }

    // Iterate over the tree for ( let x in fringe(tree) ) { trace(x); // 37, 42, "foo" }

    operator overloading

    redefine the behaviour of built-in operators for custom types

    cannot redefine operations for built-in types

    generic intrinsic function + ( list: UserList, user: User ) { list.push(user); return user; } // Usage: userList + user;

    the call stack (proper tail calls intro)

    PROPER TAIL CALLS HAVE BEEN DEFERRED

    "call stack": list of the functions currently executing

    each function call adds a new item ("stack frame") to the stack

    a stack frame indicates where to resume execution after its function exits

    when a function exits, its stack frame is removed

    function submitForm () { validateInput(); sendInputToServer(); } submitForm();

    ##[diagram] call stack: =========================== Frame: submitForm() =========================== Frame: validateInput() Frame: submitForm() =========================== Frame: sendInputToServer() Frame: submitForm() =========================== Frame: submitForm() ===========================

    proper tail calls

    THIS FEATURE HAS BEEN DEFERRED

    tail call: function calls another function as its last operation

    function getArea (w, h) { return multiply(w, h); }

    "proper tail call": run the tail-called function without adding to the call stack

    i.e., return multiply()'s result directly to getArea()'s caller

    prevents stack overflow (out of space) for recursive functions

    enables popular functional programming idioms: state machines, recursion

    meta-level hooks

    override meta::invoke to intercept method calls

    other overrides:

  • meta::get // read a dynamic variable
  • meta::set // write to a dynamic variable
  • meta::has // query a dynamic variable
  • meta::delete // remove a dynamic variable
  • meta function set(name, value) { trace("Setting " + name + " to " + value); }

    used internally to handle operations for built-in types

    reformed with

    THIS FEATURE HAS BEEN DEFERRED

    specify which properties should be found in the object

    var p = new Point(1, 2); p.x = 1; p.y = 2; var x = 10; var y = 20; // Only find x in p with (p):{x: int} { trace(x + y) // 21 }

    more numeric types

    THIS FEATURE IS IN FLUX; LIKELY REMOVAL

    ActionScript 3.0: int, uint, Number

    ECMAScript 4: int, uint, byte, double (64-bit, like old Number), decimal (128-bit, 34 digits)

    decimal context

    THIS FEATURE HAS BEEN REMOVED

    set rounding and precision options for a block of code

    // 12 digits of precision, round to even ctx = new DecimalContext(12, "half_even") { use decimal ctx // "+" computed to 12 digits of precision, // rounding to even if necessary x = a + b }

    'this' preservation

    this preserved for nested function calls

    no need to store 'this' in a local variable

    function f () { function g() { trace(this.x) } g(); // returns 'x' for f()'s 'this' } var v = { x: 10, f: f } v.f() // Displays: 10

    cast operator

    ActionScript 3.0: Apple(food)

    ECMAScript 4: food cast Apple

    'this function'

    ActionScript 3.0: arguments.callee

    ECMAScript 4: this function

    'switch type'

    switch type (v) { case (x: XML) { ... } case (s: String) { ... } // The "default" case case (o:*) { ... } }

    cases don't "fall through"

    array comprehensions

    python inspired

    convenience syntax for array initialization

    assign values from an iterator to an array's elements

    [expression loop condition]

    function range(begin, end) { for (let i = begin; i < end; ++i) { yield i; } } var tenSquares = [i*i for (i in range(0, 10))]; var tenEvenSquares = [i*i for (i in range(0, 10)) if (i%2 == 0)];

    // Create an array of Fibonacci numbers up to 1000 var fibTo1000 = [i for (i in new Fibber(1000))]

    destructuring assignment

    shortcut syntax for assignment

    assign values from an array/object to a set of variables

    e.g., assign variable values based on array elements:

    var a; var b; // Assign 1 to a, and 2 to b [a, b] = [1, 2];

    e.g., return multiple values from a function:

    function getDetails():Array { return ["Colin", "Toronto"]; } // Assign "Colin" to name and "Toronto" to city [name, city] = getDetails();

    e.g., assign variable values based on an object's variables:

    var a; var b; var o = {name:"Colin", city:"Toronto", occupation:"Author"}; // Assign "Colin" to a and "Toronto" to b {name:a, city:b} = o;

    numeric suffixes

    THIS FEATURE IS IN FLUX

    Suffixes on numeric literals denote their type:

    -7i, 1u, 37d, 14.5m

    i = int, u = uint, d = double, m = decimal

    triple-quoted strings

    THIS FEATURE HAS BEEN DEFERRED

    can contain line breaks and unescaped quote characters

    """this string starts here it has a "blank" line before this one and ends here:"""

    string indexing

    // ActionScript 3.0, yields "e" "hello".charAt(1);

    // ECMAScript 4.0, yields "e" "hello"[1]

    slicing syntax

    // ActionScript 3.0, yields "el" "hello".slice(1, 3);

    // ECMAScript 4.0, yields "el" "hello"[1:3]

    Vector class

    high-performance, monotyped array

    offers: fixed length (optional), no gaps, bounds checking

    Map class

    standard hash table

    new global function: intrinsic::hashcode

    item inspection/insertion/removal methods:

    size():uint; get(key: K):V?; put(key:K, value:V):void; has(key:K):boolean; remove(key:K):boolean;

    item iteration

    get(deep:boolean = false):iterator::IteratorType.<K>; getKeys(deep:boolean = false):iterator::IteratorType.<K>; getValues(deep:boolean = false):iterator::IteratorType.<V> getItems(deep:boolean = false):iterator::IteratorType.<[K,V]>

    reflection

    typeOf(value) returns a "meta-object" that describes value

    example meta-object APIs:

    interface ClassType extends NominalType { function construct(typeArgs: TypeIterator, valArgs: ValueIterator): Object; }

    interface NominalType extends Type { function name(): String; function namespace(): String; function superTypes(): IteratorType.<ClassType>; function publicMembers(): FieldIterator; function publicStaticMembers(): FieldIterator; }

    interface Type { function canConvertTo(t:Type): Boolean; function isSubtypeOf(t:Type): Boolean; }

    check if a value's type inherits from T:

    typeOf(value).isSubtypeOf(T)

    make a new instance of oldValue's type:

    var newValue = typeOf(oldValue).construct();

    fixed variables on generic objects

    use var to make a non-deleteable instance variable on a generic object:

    var order = {var quantity:1, var price:5.99, comment:"thanks!"); delete order.quantity // error in strict mode, otherwise no effect delete order.price // error in strict mode, otherwise no effect delete order.comment // ok, deletes the variable 'comment'

    make all instance variables non-deleteable:

    var order = var{quantity:1, price:5.99, comment:"thanks!");

    const instead of var makes the variable read-only and non-deleteable

    adds structural integrity to generic objects

    should improve performance of generic objects