ActionScript3のスコープ

他の言語の癖でなにげなく、、、ではまってしまいがちなところ。
ActionScript3でのスコープは、functionによってのみつくられるとのこと。
ちゃんと意識しておかないとクロージャを使おうとしたときなどにハマりがち。

たとえば、

var array:Array = new Array();
for(var i:int=0; i<3; i++) {
  var obj:Object = {closure: function():void {log('index:'+i);}};
  array.push(obj);
}
for each (var obj:Object in array) {
  obj.closure();  // index:0 index:1 index:2 と表示したい
}

このスクリプトを実行したとき、ログには以下のように表示される。

index:3
index:3
index:3

forはスコープをつくらないため、クロージャobj.closureのスコープ=obj.closure()を実行した時点のスコープとなる。

これを回避して、期待どおりに動作させるには以下のようにする。

var array:Array = new Array();
for(var i:int=0; i<3; i++) {
  (function():void {    // 無名関数をつくって
    var index:int = i;  // 無名関数のスコープの変数に代入
    var obj:Object = {closure: function():void {log('index:'+index);}};
    array.push(obj);
  })();                 // 呼び出す
}
for each (var obj:Object in array) {
  obj.closure();  // index:0 index:1 index:2 と表示される
}

無名関数が評価されたあとも、そのスコープの環境は残ってて、クロージャはそのスコープで評価される。この、スコープを抜けた後も、スコープの環境自体は残ってるって性質のことを静的スコープ(レキシカルスコープ)という。