You are viewing an old version of this page. View the current version.

Compare with Current View Page History

Version 1 Next »

Here, we collect all ActionScript 3 language features that are not present in ECMAScript 3 and document how we are going to simulate them.

General assumptions

We use

  • ECMAScript 5 strict mode whenever possible
  • ECMAScript 5 API (Object.create() etc.) that we know can be "polyfilled" in IE8 adequately
  • a well-known module / dependency mechanism like CommonJS modules or AMD
  • no other JavaScript library like jQuery, but "VanillaJS"

AS3 language features missing in ECMAScript 3

For every non-trivial language feature, we should introduce a dedicated child page.

Notation:

  • $0, $1, ... are auxiliary variables generated by the compiler, chosen to avoid name-clashes with any other identifiers in scope.
  • exp1, exp2, ... (italics) are arbitrary complex ActionScript expressions

Classes

Like most JavaScript frameworks, we simulate classes by a constructor function and its prototype chain.
The ES5 API Object.create(prototype,propertyDescriptors) serves this task very well.
The translation pattern is a follows:

public class Foo extends Bar {
  // constructor
  public function Foo(/*constructor parameters*/) {
    // constructor code
  }
  // member declarations
  ...
  // static member declarations
  ...
}

becomes

function Foo(/*constructor parameters*/) {
  // constructor code
}
Object.defineProperties(Foo, {
  ... // rewritten static member declarations
});
Foo.prototype = Object.create(Bar.prototype, {
  constructor: { value: Foo },
  ... // rewritten member declarations
});

The constructor property has to be defined explicitly, as otherwise, a subclass would inherit that property from its superclass, too, resulting in the subclass having the same constructor as its superclass.

Interfaces plus "as" and "is" operator

TODO

Packages

In ActionScript, packages do not exist at run-time, instead they lead to class and interface declarations having a fully qualified name to avoid name clashes.
To make a JavaScript expression referencing a fully qualified class resemble the ActionScript syntax, packages are often simulated by nested JavaScript objects, for example a class org.apache.flex.test.Foo would be assigned to a package object and then be used like so:

if (!org) org = {};
if (!org.apache) org.apache = {};
if (!org.apache.flex) org.apache.flex = {};
if (!org.apache.flex.test) org.apache.flex.test = {};
org.apache.flex.test.Foo = Foo;

var foo = new org.apache.flex.test.Foo();

(Of course, the code to create a nested package would be moved to a helper function.)
Looking at how classes are usually used in ActionScript, it turns out that they have to be imported (in contrast to Java, even if they are used by their fully-qualified names in the code!) and are only used by their fully-qualified names in the rare case of local name clashes.
Also, nested object access to fully qualified identifiers is not very efficient.
In JavaScript, there is a better solution to accessing namespaced identifiers, namely modules. There are essentially two types of module definitions: synchronous (CommonJS-style) versus asynchronous module definitions / require-calls. While synchronous requires come from the server world (e.g. NodeJS), asynchronous requires better fit realities of module loading in the browser.
When a module with a fully qualified name / path is required (loaded), it can be assigned to a local variable or a callback parameter, so that there is no need to use the fully qualified name.
Only when "exporting" ActionScript classes or other identifiers for direct use from JavaScript, it may make sense to apply the pattern "packages as global nested objects".

So the current suggestion for packages is to define a class that is inside a package as a module with the fully qualified name of that class.

Consider this class

org/apache/flex/test/Foo.as:

package org.apache.flex.test {
public class Foo {
  ...
}
}

and another class in another package using it:

org/apache/flex/test2/Baz.as:

package org.apache.flex.test2 {
import org.apache.flex.test.Foo;
public class Baz {
  ... // use Foo...
}
}

In JavaScript AMD notation, these classes would look like so:

org/apache/flex/test/Foo.js:

define("org/apache/flex/test/Foo", function() {
  function Foo() { ... }
  ...
  return Foo;
});

org/apache/flex/test2/Baz.js:

define("org/apache/flex/test2/Baz", ["org/apache/flex/test/Foo"], function(Foo) {
  function Baz() { ... }
  ... // use Foo...
  return Baz;
});

The first argument to define can be left out when the definition is the only one contained in the file, then the module name is derived from the file name.

Parameter default values

TODO

Rest (...) parameter

TODO

Bound methods

TODO

"this" is always in scope

TODO

Class member visibility (public, protected, internal, private)

TODO

Statements

TODO

for each

TODO

Operators

TODO

&&=

TODO

||=

TODO

  • No labels