イベントハンドラメソッドはdeleteで消せ! [Edit]

Flash MXから実装されたイベントハンドラメソッドの定義をクリアしたいとき、delete演算子を使わずに、undefinednullを代入する人がいます。イベントハンドラメソッドは、delete演算子で削除した方が安全です。

1. ボタンイベントハンドラメソッドのカーソル
MovieClipシンボルの第1フレームに、以下のフレームアクションを設定してみましょう。MovieClipにボタンアクション用のMovieClip.onReleaseイベントハンドラメソッドが定義されます。

スクリプト001■テスト用サンプル
// MovieClip: ボタン用
// 第1フレームアクション
this.onRelease = function() {
   trace(this);
   this.onRelease = undefined;  // [1]
   // delete this.onRelease;  // [2]
};

MovieClipインスタンス上でマウスボタンをクリックする(放す)と、MovieClipのパスを[出力]パネルに表示します。そして、このイベントハンドラメソッドにundefinedを代入していますので、メソッドに設定された(コールバック)関数はクリアされます。

ところが、もう1度このMovieClipインスタンスにマウスボタンを重ねて(ロールオーバーして)も、マウスポインタは指差しカーソルに変わります。つまり、ボタンとしての反応をしていることになります。もちろん、(コールバック)関数はクリアされていますから、クリックしてもパスは[出力]されません。

イベントハンドラメソッドにundefinedを代入した[1]のステートメントをコメントアウトし、その下のdelete演算子を使った[2]のステートメントのコメントを外してみましょう。最初のクリック時の動作([出力]パネルにパスを表示)は、先ほどのテストと変わりません。しかし、マウスポインタを再度MovieClipインスタンスに重ねて(ロールオーバーして)も、指差しカーソルには変わりません。つまり、ボタンとしての動作は完全に消滅しています。

イベントハンドラメソッドにundefinednullを代入すると、コールバック関数の定義はクリアされるので、その処理は行われなくなります。けれど、(undefinednullという)値は設定されているために、イベントハンドラメソッドが「存在」しているものとして認識されるようです。

2. 重なったインスタンスのイベント受取り
ボタンのイベント(MovieClip.onReleaseMovieClip.onPressなど)は、ひとつのインスタンスが排他的に受取ります(「onハンドラを使わずにロールオーバー/ロールアウトを検出する」参照)。 重なり合ったインスタンスがともにボタンのイベントハンドラ(メソッドまたはアクション)を定義されていた場合には、手前のインスタンスがイベントを受取り、背後のインスタンスにはイベントが渡りません。

上記のスクリプト002が設定されたMovieClipインスタンスを、いくつか複製してみましょう。そして、それらを一部重ね合わせて試してみてください。

undefinedでイベントハンドラメソッドの定義をクリアしたとき、イベントハンドラの「存在」は認識されたままでした。したがって、重なり合った手前のインスタンスのイベントハンドラメソッドがundefinedでクリアされても、イベントは相変わらず排他的に受取り、背後のインスタンスに渡しません。これは、通常意図しない結果といえるでしょう。

delete演算子でコールバック関数をクリアすれば、そのインスタンスはもはやイベントを受取らなくなります。そのため、背後にイベントハンドラを定義されたインスタンスがあれば、イベントはそのインスタンスに渡されます。

ほかにも、undefinednullを設定したプロパティ(イベントハンドラメソッドも実質はプロパティに含まれます)や変数は「存在」しているものとして、for..inループ処理や[ムービープレビュー]時([デバッグ]メニューの)[変数のリストアップ]で、値が取出されます。

したがって、イベントハンドラメソッドの「存在」を完全にクリアするには、delete演算子を使用した方がよいでしょう。

[更新] 2004年6月13日「2.重なったインスタンスのイベント受取り」を追加し、文章を一部修正

コメント

この記事へのコメント

  1. 1.youich(2004年06月23日 15:36)

    タイムラインやステージで扱われるようなイベントハンドラメソッド
    (インスタンスレベルのプロパティ)に関してはdeleteキャンペーンでもよいと思うのですが、
    asファイルでのスクリプティングをする場合を考えるとdeleteではイベントを無効にすることは
    できませんよね?__proto__.onEnterFrameをdeleteすることは無しにして(^^;


    class MyClip extends MovieClip{
    function onEnterFrame(){
    _xscale = _yscale += 10;
    if(_width>Stage.width/2){
    delete(this.onEnterFrame);
    //this.onEnterFrame = null;
    }
    }
    }
    これはインスタンスレベルでonEnterFrameをオーバーライドするというか
    インスタンスプロパティのonEnterFrameにnull代入することでイベントを受け付けなく
    なってるということなんですが、

    「イベントハンドラメソッドはdeleteで消せ」キャンペーンをしてしまうと
    この辺りの違い、、prototype、__proto__プロパティと
    instanceプロパティの関係があいまいな方は混乱するということはないでしょうか?


    コードをポストする時の整形のタグってあります?

  2. 2.youich(2004年06月23日 15:43)

    スペースを” ”に置換してみましたが、、
    こんなやり方じゃないですよね?

    class MyClip extends MovieClip{
        function onEnterFrame(){
            _xscale = _yscale += 10;
            if(_width>Stage.width/2){
                delete(this.onEnterFrame);
                //this.onEnterFrame = null;
            }
        }
    }

  3. 3.野中 文雄(2004年06月26日 19:12)

    この記事で述べた内容は、定石あるいは「車は左、人は右」といったルールのようなものです。定石どおりで勝てるなら、棋士は存在しえません。ルールも杓子定規に当てはめれば、逆に危険なこともあります。しかし、通常の場合これらに従うと、予想外の結果に慌てる状況を減らせます。

    ActionScript 2.0では、クラスに定義したメソッドは削除できません。しかし、MovieClip.onEnterFrameイベントハンドラメソッドなどのコールバック関数を、動的に設定・削除する必要があるなら、つぎのようにクラスを定義する方法もあります。

    // ActionScript 2.0クラス定義ファイル: MyClip.as
    class MyClip extends MovieClip {
      function MyClip() {
        this.onEnterFrame = myOnEnterFrame;
      }
      function myOnEnterFrame() {
        _xscale = _yscale += 10;
        if (_width>Stage.width/2) {
          delete (this.onEnterFrame);
          //this.onEnterFrame = null;
        }
      }
    }

    もっとも、どちらの定義方法をとるかは、スクリプトの仕様・デザインによって決定されるべきことです。クラスを定義するようになったら、もはや単純に定石やルールに従うというのでなく、処理の意味や結果を判断してスクリプティングを行うことが望ましいでしょう。prototypeとか、クラスとインスタンスのメンバ(プロパティ)の扱いについては、この段階で勉強されればよいと思います。

    なお、コード用の整形タグはありませんので、私は「 」を挿入しています。
    _____
    > コードをポストする時の整形のタグってあります?

その他の記事