In my last post about static initializers I talked about member initializations with side effects, which need to be neutralized. This was one of the examples for “safe” initializers without any side effects:
// ActionScript: public class Foo { public const m_instance_member : String = "Instance Member"; }// JavaScript: var Foo = function() {}; Foo.prototype.m_instance_member = "Instance Member";
The example above used a String on purpose. As it turns out Arrays and Objects are not always safe and may introduce side effects in situations you might not expect.
Ninjas are Persons too
I have a simple question for you: Are Arrays safe, too?
// ActionScript:
public class Person
{
public var m_array : Array = [];
}
Looks pretty safe, doesn’t it? Shouldn’t we transform that snippet as follows?
// JavaScript:
var Person = function() {};
Person.prototype.m_array = [];
That also looks okay. But problems appear when you use Person as a base class for another class.
// ActionScript: public class Ninja extends Person { }
// JavaScript: var Ninja = function() {}; goog.inherits( Ninja, Person );
What can possibly be wrong with this transformation? Well, try this code:
// ActionScript: var person : Person = new Person(); var ninja : Ninja = new Ninja(); person.m_array.push(123); trace( ninja.m_array.length );
// JavaScript: var person = new Person(); var ninja = new Ninja(); person.m_array.push(123); trace( ninja.m_array.length );
We create an instance of Person and an instance of Ninja. Then we add the number 123 to person‘s m_array and print out the length of ninja‘s m_array. The expected result should be of course 0. So, what’s the problem? Unfortunately, if we don’t treat Array as an initializer with side effects the generated code will yield 1 instead of 0. For some odd reason adding 123 to person‘s m_array somehow affects ninja‘s m_array as if m_array were shared between the two classes.
Closure-library’s Issue 396
Last month I wrote up this bug against Google’s Closure Library, which was filed as issue 396:
Issue 396: goog.inherits() does not clone arrays and objectsWhat steps will reproduce the problem? 1. Run this code, which uses goog.inherits var Person = function() {}; Person.prototype.m_array = []; var Ninja = function() {}; goog.inherits( Ninja, Person ); var p = new Person(); var n = new Ninja(); p.m_array.push(123); console.info( n.m_array ); 2. Observe result in the debug console Result is: "[123]" What is the expected output? What do you see instead? "[]"
Nicholas J. Santos: right, this is working as intended. All objects on the prototype are shared. I believe Closure Linter will warn you about this if you do it.
Jay Young: It sounds like you expect goog.inherits to copy members from one class to another (also known as mixins or composition-based inheritance). That is not the case. Closure uses true prototypal inheritance. Anything on a superclass prototype is shared with all subclasses as well. Closure style has a rule that only primitive values should be placed on the prototype chain. Most often, the intention is to use an empty object/array as a default, but what ends up happening is exactly what you described: methods end up mutating the prototype object instead of an instance-specific one.
Neutralizing Arrays
// ActionScript:
public class Person
{
public var m_array : Array = [];
}
public class Ninja extends Person { }
var person : Person = new Person(); var ninja : Ninja = new Ninja(); person.m_array.push(123); trace( ninja.m_array.length ); // Expected: 0 and not 1
And here is the transformed JavaScript:
// JavaScript: var Person = function() { var self = this; self.m_array = []; };Person.prototype.m_array = null;
var Ninja = function() {}; goog.inherits( Ninja, Person );
var person = new Person(); var ninja = new Ninja(); person.m_array.push(123); trace( ninja.m_array.length ); // Expected: 0 and not 1
Conclusion
Arrays and Objects introduce side effects when used as static initializers in base classes, because most JavaScript libraries chain prototypes for emulating inheritance. For that reason I recommend adding Arrays and Objects to the list of expressions with side effects when used as static initializers, which need to be neutralized.

Hey, I think your site might be having browser compatibility issues. When I look at your blog in Safari, it looks fine but when opening in Internet Explorer, it has some overlapping. I just wanted to give you a quick heads up! Other then that, wonderful blog!
Thanks for the warm words and pointing out those problems in IE. I will check with our IT department. But a lot of folks are already on their Holiday break. This might not get resolved until the new year.
Hi all,
About this cross compilation issue.
I have more global questions which are very important for strategic future of the flash platform.
You’ talking about FalconJS and cross compilation to actual JS, aka EcmaScript 3.
But the near future and the very usefull cross compilation of AS3 for mobile web browsers where theres no flash player, will be for sure the cross compilation to Ecmascript 6. would’nt it?
So i wonder:
- how is it difficult to cross compile AS3 to ES6? what performances drawbacks it leads to?
- what is the gap between ES6 and ES4 and thus, how long will it take for standards to evolve from ES6 to next version?
- what the drop of packages, namespaces and early bindings imply for AS3 cross compilation and performances?
Thanks a lot, for the answers which are very very important to understand where flash platform and AS3 coding can lead to the near future.
Hello Rahan,
FalconJS is designed to generate JavaScript, that can be processed by most modern browsers like Safari, Chrome, Firefox, and IE9 (sorry, but IE6 doesn’t make the cut). Early versions of FalconJS used ES5′s getter/setter but that feature turned out to create massive performance problems. FalconJS currently identifies getter/setter at compile time and injects appropriate calls, which avoids the performance problems of ES5′s getters and setters.
In general I would say that adjusting FalconJS to emit ES5 or ES6 JavaScript should not be difficult. But I suspect that in many cases it pays off to let FalconJS statically analyze the code and emit code that avoids ES5 and ES6 features. Please keep in mind that many of those new ES5/ES6 features don’t come for free.
It is quite possible that you might have to pay for the convenience of using ES6 features like proxy, or ES5 features like getters/setters with some significant loss of performance. Let’s hope that that will only be the case in the first years…
In regards to avoiding resolving names at runtime to increase performance (if that was one of you question) I propose using the Dart development model, where in “checked mode” FalconJS would emit fairly expensive code, which would throw asserts but which would get stripped out in “production mode”. I discussed this idea in my very first blog post called “Dart and Types: Tennis without a net?” (http://blogs.adobe.com/bparadie/2011/11/16/dart-and-types-tennis-without-a-net/).
Bernd, just wanted to mention that Jangaroo has been applying this pattern for initializing fields with Arrays or Objects (or in fact anything that is not immutable) from the very beginning. I’ll try and put together a blog post about how Jangaroo even keeps the initializing code at the same line number in the generated output.