Hi folks, as mentioned in another thread, I have been working on a new JavaScript "runtime", which is what I call the way the generated JavaScript code looks and works like. While this thread's title has [FalconJx] in it, there is no reason the proposal could not be used for FalconJS, too, but I understood that for now, using Google Closure is a given.
I had the following design goals: - Support ActionScript 3's "differentiator" features: private members, interfaces + is/as, lazy class initialization, method binding, getter/setter functions - work in IE < 9 (except for getter/setter functions) - Produce debuggable JavaScript code that can also be optimized for deployment. The debuggable code should be very similar to the original ActionScript source code for every line containing executable code - Dependency management: load/bundle only the needed classes. Every compilation unit to debug should load as a separate request - Minimal to no runtime overhead, even in debug mode - Support trace() I took the following design decisions: - For private members, a mix of JavaScript lexical scope and field renaming like in Jangaroo is used - To implement an efficient "is" operator, all interfaces a class implements are determined once for the class. I use a prototype chain to inherit the interfaces implemented by the super class - Lazy class initialization uses a very efficient technique refined by Olaf Kummer: Wherever a constructor or non-private static member is called, the "self-destructing" class initialization function is checked for existence and, if still existing, called ("guarded" call) - Method binding is again borrowed from the Jangaroo Runtime: the bound function is "cached" at the object under a different but unique name so that it keeps its identity. This is important e.g. to be able to remove event listeners - Getter/setter functions map to Object.defineProperty("...", { get: ..., set: ...}), thus no support in IE < 9 - To make everything else work in IE < 9, I use polyfills for ECMAScript 5 Object, Array and Function API - For keeping generated code in separate JS files, expressing dependencies between these files, loading them separately at runtime and linking them statically into a single JavaScript file, I chose to use Asynchronous Module Definitions (AMD) and RequireJS (version 2.1.2) as the concrete implementation of AMD. AMD is preferred over CommonJS because it better fits web applications, as e.g. argued here: http://blog.millermedeiros.com/amd-is-better-for-the-web-than-commonjs-modules/To link and optimize the application, RequireJS provides an optimizer tool simply called "r.js". - To conditionally load polyfills (for IE < 9), I use the "shim" RequireJS plugin - To achieve minimal runtime overhead, I only use helper functions to define the class structure once (defineClass and defineInterface). As soon as the complete application structure is set up, all code executed is inlined - Since all modern browsers seem to support a "console" object with a "log" method, trace() is simply implemented to check for the existence of console.log() by trial-and-error (try... catch). It then String-converts and concatenates all its arguments like its ActionScript sibling *About the Example* To specify how I think FalconJx-generated JavaScript output should look like, I created an ActionScript example, consisting of two classes and three interfaces. Every language feature supported by the new runtime format gives an appearance in the code (well, I didn't add a private static method, because it works like a private method, only simpler). The whole thing is a public GitHub project at https://github.com/fwienber/as-js-runtime-prototype. To download all files in one go as a zip or tar.gz, please use the tag page: https://github.com/fwienber/as-js-runtime-prototype/tags. A live demo is available on the corresponding GitHub pages at http://fwienber.github.com/as-js-runtime-prototype/index.html >From there, you can reach all ActionScript example sources and the proposed JavaScript equivalent (so far, hand-coded :-)). To see the example in action, use the three bottom links which start the same application in three flavors: - debug: Each JS file corresponding to an AS class or interface and the main bootstrap file is loaded with a single request. This gives you a nice 1:1 mapping of source to output in the JavaScript debugger. Additionally, there are some "infrastructure" files: require.js itself, the RequireJS plugin "shim" (shim.js, shims.js), my AS3 runtime helpers defineClass.js and defineInterface.js, and a couple of built-in AS3 functions (bind, as, is, trace). If loading in IE < 9, the shim plugin loads several ES5 polyfills. - linked: The whole application is merged into one JS file using the RequireJS optimizer "r.js" without optimizations. Thus you only have two requests: require.js and hello-worls-all.js are loaded. In IE < 9, the polyfills are still loaded one-by-one (I didn't bother to optimize this case, but of course, it can be done). - minified: Same as "linked", only that require.js and hellow-world-all.js are minified by Uglify2. This can easily be achieved using "r.js" with the parameter optimize=uglify2. For the demo, I redirected trace() from the JavaScript console (backed-up in trace-console.js) to the DOM, so that you can see the log messages directly. Just look into the code and observe how it works in Firebug or any other JavaScript debugger of your choice, and let me know if anything is unclear, missing or if you need more reasoning for the design decisions taken. I know there are still several issues to be fleshed out (e.g. interaction with "native" JavaScript or package-scope variables, just to name two), but I think the current state is a solid core of how a modern, efficient, self-contained, extensive and still concise JavaScript runtime format for Apache Flex could look like. Please tell me what you think! Have fun, -Frank-