views:

633

answers:

3

I'm clearly missing something here.

I need to fill methods of dynamic AS3 class from an array (see silly example below).

But when I call those methods, all of them appear to be the same method. In the example below, all methods are foobar1.

If I create methods by hand, without a loop, everything is fine.

Any clues?

  package foo
  {
    public class Bar
    {
      public function testDynamicClassSanity():void
      {
        var foo:Foo = new Foo();
        var methods:Object = { foobar1: 101, foobar2: 201, foobar3: 301 };

        for (var key:String in methods)
        {
          var val:Number = methods[key];
          foo[key] = function():Number
          {
            return val;
          };
        }

        // Next trace prints
        // 101 = 101 201 = 101 301 = 101
        trace(
            101, "=", foo.foobar1(),
            201, "=", foo.foobar2(),
            301, "=", foo.foobar3()
          );
      }
    }
  }

  internal dynamic class Foo
  {
  };
+2  A: 

I would guess the problem is in the scoping of val -- you assume its scope is the for loop, but that is not the case in AS3, the scope is the function. Am I correct that all your calls return 301?

Update: As working around the issue that you are experiencing (the variable val being referenced and only later resolved instead of being 'copied' into your function) is quite cumbersome: Depending on your use case you could inspect the method calls and just look up the desired result in the table using the functionality provided by Proxy.

Simon Groenewolt
Yes, you're correct (almost, it is 101). I've updated the question. How to work around this then?
Alexander Gladysh
Ah, I was assuming the for loop would iterate over the properties of the object in the order they were defined, that was of course silly to assume :-) Check this other question http://stackoverflow.com/questions/422784/how-to-fix-closure-problem-in-actionscript-3-as3 for a tip.
Simon Groenewolt
+1  A: 

I think that your problem is the scoping of the var variable. Try this modification:

for (var key:String in methods)
{
  var val:Number = methods[key];
  foo[key] = function (valInternal:Number) {
    return function():Number
    {
        return valInternal;
    };
  }(val);
}

(the above trick works in Javascript to workaround a similar problem... I bet it is applicable to AS3)

jsight
I believe it should be "return valInternal", right?
Alexander Gladysh
yes, fixed.....
jsight
+1  A: 

For the records, I'll post here the corrected version of the testDynamicClassSanity() function:

    public function testDynamicClassSanity():void
    {
      var foo:Foo = new Foo();
      var methods:Object = { foobar1: 101, foobar2: 201, foobar3: 301 };

      // We have to introduce new scope
      var makeCallback:Function = function(result:Number):Function
      {
        return function():Number
        {
          return result;
        }
      }

      for (var key:String in methods)
      {
        foo[key] = makeCallback(methods[key])
      }

      // Prints as intended
      // 101 = 101 201 = 201 301 = 301
      trace(
          101, "=", foo.foobar1(),
          201, "=", foo.foobar2(),
          301, "=", foo.foobar3()
        );
     }
Alexander Gladysh