サイトトップ

Director Flash 書籍 業務内容 プロフィール

Adobe Flash非公式テクニカルノート

関数・メソッドとthis

ID: FN0803001 Product: Flash Platform: All Version: CS3 and above/ActionScript 3.0

オブジェクトに設定した関数やメソッド内におけるthis参照は、ActionScript 3.0では2.0とは仕様が変わりました。本稿はその違いについて、簡単に説明します。


1. フレームアクションに定義した関数内のthis
まず、ActionScript 2.0でインスタンスに対して関数を設定するのは、イベントハンドラメソッドの定義が典型です。オブジェクトに関数を設定すると、その中におけるthisは関数の設定されたインスタンスを参照します。他方、フレームアクションに定義した関数内で参照先を指定しないと、スクリプトを記述したタイムラインが自動的にターゲットとして認識されます(「Buttonのthis」参照)。

イベントハンドラメソッドはActionScript 3.0にはありませんので、比較ができるように2.0で以下のようなフレームアクションを書いてみます(スクリプト001)。タイムラインにはMovieClipインスタンスmy_mcを配置しておきます。また、[パブリッシュ設定]で、[ActionScriptのバージョン]は[ActionScript 2.0]を選びます。

スクリプト001■関数をMovieClipインスタンスに設定して呼出し結果を比較 ー ActionScript 2.0

// ActionScript 2.0
// フレームアクション
var name_str:String = "main";
my_mc.name_str = "my_mc";
function xTest():Void {
  trace([name_str, this.name_str]);
}
my_mc.xTest = xTest;
// [1]直接関数を呼出し
xTest();   // 出力: main,main
// [2]MovieClipインスタンスに設定した関数を呼出し
my_mc.xTest();   // 出力: main,my_mc

関数xTest()を直接呼出すと、thisがあってもなくてもスクリプトを記述したタイムラインが参照され、フレームアクションに宣言したタイムライン変数name_strの値"main"が[出力]されます。

ところが、関数xTest()をMovieClipインスタンスmy_mcに設定して、その関数を呼出すと、this参照のあるなしで値の[出力]される変数が変わります。thisは関数の設定されたインスタンスmy_mcを参照するので、my_mcに設定した変数name_strの値"my_mc"が[出力]されます。ところが、thisを指定しないとスクリプトを記述したタイムラインが参照され、タイムライン変数として宣言したname_strの値"main"が取出されるのです。

同じ内容のスクリプトを、ActionScript 3.0で試してみましょう(スクリプト002)。ActionScript 2.0(スクリプト001)との違いは、関数の戻り値の型指定を頭文字が小文字のvoidに変えただけです。

スクリプト002■関数をMovieClipインスタンスに設定して呼出し結果を比較 ー ActionScript 3.0

// ActionScript 3.0
// フレームアクション
var name_str:String = "main";
my_mc.name_str = "my_mc";
function xTest():void {
  trace([name_str, this.name_str]);
}
my_mc.xTest = xTest;
// [1]直接関数を呼出し
xTest();   // 出力: main,main
// [2]MovieClipインスタンスに設定した関数を呼出し
my_mc.xTest();   // 出力: main,main

ActionScript 3.0では、thisがあってもなくても、また関数xTest()を直接実行しても、MovieClipインスタンスmy_mcに設定して呼出しても、[出力]されるのはつねにフレームアクションにタイムライン変数として宣言されたname_strの値"main"になります(図001)。これは関数内のスコープ(参照される範囲)[*1]thisのあるなしに拘らず関数の定義されたインスタンスになり、関数が他のオブジェクトに設定されてもスコープは変わらないからです。

図001■ActionScript 3.0では関数内のスコープが変わらない

thisの有無や関数xTest()の設定先に拘らず、関数の定義されたタイムライン変数を参照する。

[*1] ActionScript 3.0における関数のスコープについては、[ヘルプ]の[ActionScript 3.0のプログラミング] > [ActionScript言語とシンタックス] > [関数] > [関数のスコープ]をご参照ください。


2. クラスに定義したメソッド内のthis
クラスに定義したメソッド内では、ActionScript 3.0も2.0も、参照としてthisは自動的に補われます。すると、ActionScript 2.0では、thisを省略してもフレームアクションのように参照先が変えられません。そのため、クラスでMovieClipインスタンスをプロパティに保持して、イベントハンドラメソッドによりコントロールしようとしたとき、問題が生じることになります(詳しくは、「インスタンスのプロパティでないのに宣言を求められる」参照)。

ActionScript 2.0にはこのためにDelegateクラスが備わり()、メソッドのthis参照先を変えることができます。以下のテスト用クラスScopeTest(スクリプト003)は、コンストラクタに引数としてMovieClipインスタンスを渡すと、MovieClip.onEnterFrameイベントハンドラメソッドが設定されて、インスタンスを回転させます。クラスScopeTestのコンストラクタは、たとえばフレームアクションからタイムラインに配置したMovieClipインスタンスmy_mcを引数にして、つぎのように呼出します。

// フレームアクション
// MovieClipインスタンスmy_mcを配置
var obj:ScopeTest = new ScopeTest(my_mc);
スクリプト003■MovieClipインスタンスに回転のアニメーションを設定するクラス ー ActionScript 2.0

// ActionScript 2.0クラス定義ファイル: ScopeTest.as
import mx.utils.Delegate;
class ScopeTest {
  private var target_mc:MovieClip;
  public function ScopeTest(_mc:MovieClip) {
    target_mc = _mc;
    _mc.onEnterFrame = Delegate.create(this, rotate);
  }
  private function rotate():Void {
    target_mc._rotation += 10;
  }
}

コンストラクタに渡された引数のMovieClipインスタンスに対してMovieClip.onEnterFrameイベントハンドラメソッドを設定するとき、コールバック関数にDelegate.create()メソッドを適用してthis参照をクラスインスタンスに変更していることにご注目ください。this参照を変えないと、コールバック関数rotate()内の参照にはthisが補われて、メソッド内のステートメントはつぎの記述と同じになってしまいます。

this.target_mc._rotation += 10;

ActionScript 2.0では、イベントハンドラメソッドに設定したコールバック関数内のthisは、メソッドの設定されたインスタンスを参照します。つまり、前掲クラスScopeTest(スクリプト003)のrotate()メソッド内のthisは、MovieClipインスタンスtarget_mcになり、上記ステートメントはtarget_mc内の識別子target_mcを探してしまうことになります。

この問題を解決するために、Delegate.create()メソッドにより、rotate()メソッド内のthisがScopeTestインスタンスを参照するよう変更し、正しくMovieClipインスタンスtarget_mcをコントロールできるようにする必要があるのです。

それに対して、ActionScript 3.0では、メソッド内のthisはつねにそれが定義されているインスタンスを参照します(this参照を省略した場合も、2.0と同じくthisが補われたものとして扱われます)[*2]。したがって、とくに参照を変更するという操作は必要ありません。

前掲ActionScript 2.0によるスクリプト003と同じ内容を、3.0で定義したクラスScopeTestはつぎのようになります(スクリプト004)。

スクリプト004■MovieClipインスタンスに回転のアニメーションを設定するクラス ー ActionScript 3.0

// ActionScript 3.0クラス定義ファイル: ScopeTest.as
package {
  import flash.display.MovieClip;
  import flash.events.Event;
  public class ScopeTest {
    private var target_mc:MovieClip;
    public function ScopeTest(_mc:MovieClip) {
      target_mc = _mc;
      _mc.addEventListener(Event.ENTER_FRAME, rotate);
    }
    private function rotate(eventObject:Event):void {
      target_mc.rotation += 10;
    }
  }
}

DisplayObject.enterFrameイベントにリスナー関数rotateを登録する際も、rotate()メソッド内も、とくに参照を変更することなく、ScopeTestインスタンスのプロパティtarget_mcを参照することができています。つぎのフレームアクションを記述して[ムービープレビュー]を確かめれば、タイムラインに配置したインスタンスmy_mcに回転のアニメーションが設定されます(図002)。

// フレームアクション
// MovieClipインスタンスmy_mcを配置
var obj:ScopeTest = new ScopeTest(my_mc);
図002■コンストラクタに渡されたインスタンスに対して回転のアニメーションを設定

イベントリスナーの登録時もrotate()メソッド内も、とくに参照を変更する処理は不要。インスタンスプロパティが正しく参照できる。

[*2] ActionScript 3.0のメソッド内におけるthis参照については、[ActionScript3.0のプログラミング] > [ActionScriptのオブジェクト指向プログラミング] > [クラス] > [メソッド]の「バインドメソッド」およびakihiro kamijo「thisとクロージャ」をご参照ください。


3. 名前のない関数内のthis
ActionScript 3.0でも、名前のない関数を定義した場合には、それを設定したオブジェクトがthisキーワードにより参照されます。したがって、以下のフレームアクション(スクリプト005)のように、名前のない関数をタイムラインの変数とMovieClipインスタンスにそれぞれ設定すると、this参照は異なる結果になります。タイムライン変数に設定した関数からはthisがそのタイムラインになり、MovieClipインスタンスをターゲットに呼出した関数内では設定されたインスタンスがthisとして参照されます(図003)。

スクリプト005■名前のない関数をタイムラインとMovieClipインスタンスに設定 ー ActionScript 3.0

// ActionScript 3.0
// フレームアクション
var name_str:String = "main";
my_mc.name_str = "my_mc";
var xTest:Function = function ():void {
  trace([name_str, this.name_str]);
};
my_mc.xTest = xTest;
// [1]直接関数を呼出し
xTest();   // 出力: main,main
// [2]MovieClipインスタンスに設定した関数を呼出し
my_mc.xTest();   // 出力: main,my_mc


図003■名前のない関数内では設定されたオブジェクトがthisとして参照される

thisは名前のない関数の設定先に応じて、タイムライン変数に格納された関数内からはそのタイムライン、MovieClipインスタンスがターゲットとして呼出されるとそのインスタンスを参照する。

なお、ActionScript 3.0にも2.0と同じく、this参照を変更するFunction.apply()Function.call()メソッドが備わっています。しかし、タイムラインやクラスに定義した関数・メソッドのthis参照は、これらのメソッドを用いても変えることはできません。Function.apply()Function.call()メソッドは、名前のない関数についてのみ適用することが可能です。

スクリプト006■名前のない関数に対してFunction.apply()メソッドを呼出す ー ActionScript 3.0

// ActionScript 3.0
// フレームアクション
var name_str:String = "main";
my_mc.name_str = "my_mc";
var xTest:Function = function ():void {
  trace([name_str, this.name_str]);
};
xTest.apply(my_mc)   // 出力: main,my_mc;


作成者: 野中文雄
作成日: 2008年3月31日


Copyright © 2001-2008 Fumio Nonaka.  All rights reserved.