JavaScript Method Closures
I was further convinced this week that the world needs JASPA when my Flash developer colleague asked me about closures in JavaScript. He’s been knee-deep in AS3 for almost a year, so JavaScript must seem rather weak now.
Unlike JavaScript and AS2, AS3 supports method closures implicitly; so I made sure they were built into the JAS language. Such a thing is not native to JavaScript so the JASPA compiler automatically generates a call to helper function jaspa.closure whenever an object’s method is referenced.
If you’ve ever used Delegate.create in AS2, then you’ve encountered this issue. If you don’t see the problem, consider this example in JavaScript:
- var Cat = function(){
- this.voice = ‘Miaow’;
- }
- Cat.prototype.speak = function(){
- alert( this.voice );
- }
- var Sammy = new Cat;
- Sammy.speak(); // alerts “Miaow”
- var Callback = Sammy.speak;
- Callback(); // alerts undefined
As soon as we’ve created a reference to the function member, we’ve severed it from its owner, so when we call it, it’s not being called on the object. In JavaScript and AS2 we need to use a method like Delegate.create to explicitly create a closure. Here’s an example:
- function makeClosure( obj, func ){
- var closure = function(){
- return func.apply( obj, arguments );
- }
- closure.apply = function( obj, args ){
- return func.apply( obj, args );
- }
- closure.call = function( obj ){
- var args = [];
- for( var i = 1; i < arguments.length; i++ ){
- args.push( arguments[i] );
- }
- return func.apply( obj, args );
- }
- return closure;
- }
It’s basically a wrapper function which seals in the original arguments. Additionally apply and call must be overridden or calling them directly will spoil the party. Try it with the Cat example and you’ll see the desired result.
- var Callback = makeClosure( Sammy, Sammy.speak );
- Callback(); // alerts “Miaow”
There is a problem doing this with native functions in IE
http://web.2point1.com/2008/12/28/ie-native-functions-bug/
Comparing identical method closures is also a problem when they are actually separate instances. A valueOf function needs to be added to the closure to provide a unique identifier for testing whether two are the same.
See much more extensive article on the JASPA WIKI:
http://jaspa.org.uk/wiki/Method_closure