パッケージと名前空間

パッケージと名前空間は関連する概念です。 パッケージを使用すると、コードを共有でき、名前のコンフリクトを最小限に抑えられるようにクラス定義をバンドルできます。 名前空間を使用すると、プロパティ名やメソッド名などの識別子の可視性を制御でき、コードがパッケージ内部にあるのか外部にあるのかに関係なく適用できます。 パッケージを使用してクラスファイルを構成でき、名前空間を使用して個々のプロパティおよびメソッドの可視性を制御できます。

パッケージ

ActionScript 3.0 では、パッケージは名前空間で実装されますが、名前空間と同義ではありません。 パッケージを宣言すると、コンパイル時に必ず既知である名前空間が暗黙的に作成されます。 名前空間は、明示的に作成された場合はコンパイル時に既知である必要はありません。

次の例では、package ディレクティブを使用して、クラスを 1 つ含む単純なパッケージを作成します。

package samples 
{ 
    public class SampleCode 
    { 
        public var sampleGreeting:String; 
        public function sampleFunction() 
        { 
            trace(sampleGreeting + " from sampleFunction()"); 
        } 
    } 
}

この例のクラスの名前は SampleCode です。 クラスは samples パッケージ内にあるので、コンパイラーはコンパイル時にクラス名を自動的に修飾し、完全修飾名の samples.SampleCode にします。 また、コンパイラーはプロパティやメソッドの名前も修飾するので、sampleGreeting および sampleFunction() は、それぞれ samples.SampleCode.sampleGreeting および samples.SampleCode.sampleFunction() になります。

多くの開発者、特に Java 開発者は、パッケージのトップレベルにクラスだけを配置することを選択します。 しかし、ActionScript 3.0 では、パッケージのトップレベルでクラスだけではなく、変数、関数、ステートメントもサポートされています。 この機能の高度な使用方法として、パッケージ内のすべてのクラスで使用できるように、パッケージのトップレベルで名前空間を定義できます。ただし、パッケージのトップレベルで指定できるアクセス指定子は publicinternal の 2 つだけです。ネストされたクラスをプライベート宣言できる Java と異なり、ActionScript 3.0 はネストされたクラスも、プライベートクラスもサポートしていません。

しかし、その他の多くの点で、ActionScript 3.0 のパッケージは Java のパッケージと似ています。 前の例を見ればわかるとおり、完全修飾パッケージ参照は、Java と同じようにドット演算子(.)を使用して表します。パッケージを使用すると、他のプログラマーも使用できるように、直感的な階層構造にコードを構成できます。 これにより、独自のパッケージを作成して他のプログラマーと共有したり、他のプログラマーが作成したパッケージを利用したりでき、コードの共有が容易になります。

パッケージを使用することで、使用する識別子名が一意になり、他の識別子名と競合しなくなります。 実際、これがパッケージの最も重要な利点であるという人もいます。 例えば、コードを共有しようとしている 2 人のプログラマーがそれぞれ SampleCode というクラスを作成したとします。パッケージを使用しなければ、名前の競合が発生し、いずれかのクラスの名前を変更するしかなくなります。 ただし、パッケージを使用すると、一意の名前を持つパッケージ内にクラスのいずれか、または両方をそれぞれ配置することによって、名前の競合は簡単に回避されます。

パッケージ名に埋め込みドットを入れて、ネストされたパッケージを作成することもできます。 これにより、パッケージの階層構造を作成できます。 これを示す適当な例は、ActionScript 3.0 で提供される flash.display パッケージです。flash.display パッケージは flash パッケージ内にネストされています。

ActionScript 3.0 のほとんどは、flash パッケージで編成されています。例えば、flash.display パッケージには表示リスト API が含まれ、flash.events パッケージには新しいイベントモデルが含まれています。

パッケージの作成

ActionScript 3.0 では、パッケージ、クラス、およびソースファイルを整理する方法に大きな柔軟性を持たせています。 旧バージョンの ActionScript では、ソースファイルごとにクラスが 1 つだけ許可され、ソースファイルの名前がクラスの名前と一致する必要がありました。ActionScript 3.0 では、1 つのソースファイルに複数のクラスを含めることができますが、ファイルの外部にあるコードで使用できるのは各ファイルのクラス 1 つだけです。 つまり、各ファイルのクラスを 1 つだけパッケージ宣言内で宣言できます。 その他のクラスはパッケージ定義の外部で定義する必要があります。これにより、追加されるクラスはソースファイル外部にあるコードに対して表示されなくなります。 パッケージ定義内で定義されたクラスの名前は、ソースファイルの名前に一致している必要があります。

ActionScript 3.0 では、パッケージをより柔軟に宣言することができます。 旧バージョンの ActionScript では、パッケージはソースファイルを配置するディレクトリを表すだけでした。また、package ステートメントでパッケージを宣言するのではなく、クラス宣言内の完全修飾クラス名の一部としてパッケージ名を含めていました。ActionScript 3.0 でもパッケージはディレクトリを表しますが、パッケージに含めることができるのはクラスだけではありません。 ActionScript 3.0 では、package ステートメントを使用してパッケージを宣言します。つまり、パッケージのトップレベルで変数、関数、および名前空間を宣言することもできます。パッケージのトップレベルに実行可能ステートメントを含めることもできます。 パッケージのトップレベルで変数、関数、または名前空間を宣言した場合は、そのレベルで使用できる属性は public および internal だけです。また、この宣言がクラス、変数、関数、または名前空間のいずれであるかに関係なく、public 属性を使用できるのはファイルごとにパッケージレベルの宣言 1 つだけです。

パッケージは、コードを構成したり、名前のコンフリクトを防いだりするのに便利です。 パッケージの概念および関連のないクラス継承の概念を混同しないようにしてください。 同じパッケージ内にある 2 つのクラスには共通する名前空間がありますが、この 2 つのクラスはその他で関連しているとは限りません。同様に、ネストされたパッケージは、親パッケージと意味的な関係がない場合があります。

パッケージの読み込み

パッケージ内部にあるクラスを使用する場合、パッケージまたは使用するクラスを読み込む必要があります。 これは、クラスの読み込みがオプションであった ActionScript 2.0 とは異なります。

例えば、前述の SampleCode クラスの例を考えてみます。クラスが samples という名前のパッケージにある場合、SampleCode クラスを使用する前に次の import ステートメントのいずれかを使用する必要があります。

import samples.*;

または

import samples.SampleCode;

一般的に、import ステートメントは可能な限り明確にします。samples パッケージから SampleCode クラスだけを使用する場合、SampleCode クラスが属するパッケージ全体ではなく、このクラスだけを読み込むようにしてください。パッケージ全体の読み込みにより、予期しない名前の競合が発生する場合があります。

クラスパス内にパッケージまたはクラスを定義するソースコードを配置する必要もあります。クラスパスは、ローカルディレクトリパスのユーザー定義リストです。このパスは、コンパイラーが読み込むパッケージとクラスを検索する場所を指定します。クラスパスはビルドパスまたはソースパスと呼ばれることもあります。

クラスまたはパッケージを適切に読み込むと、クラスの完全修飾名(samples.SampleCode)またはクラス名のみ(SampleCode)のいずれかを使用できます。

完全修飾名は同じ名前のクラス、メソッド、またはプロパティが存在してコードがあいまいになるときに役立ちますが、すべての識別子に使用すると管理が困難になる可能性があります。 例えば、SampleCode クラスのインスタンスをインスタンス化するときに完全修飾名を使用すると、コードが冗長になります。

var mySample:samples.SampleCode = new samples.SampleCode();

ネストされたパッケージのレベルが高くなるにつれて、コードの可読性が低下します。 あいまいな識別子が問題ではないことが確実な場合、単純な識別子を使用してコードを読みやすくすることができます。例えば、クラス識別子のみを使用する場合、SampleCode クラスの新しいインスタンスのインスタンス化はかなり簡単になります。

var mySample:SampleCode = new SampleCode();

最初に適切なパッケージまたはクラスを読み込まずに識別子の名前を使用しようとしても、コンパイラーはクラス定義を検出できません。また、最初にパッケージまたはクラスを読み込んだ場合でも、読み込んだ名前と競合する名前を定義しようとすると、エラーが生成されます。

パッケージの作成時には、そのパッケージのすべてのメンバーのデフォルトのアクセス制御子は internal です。この場合、デフォルトではパッケージメンバーは、そのパッケージの他のメンバーに対してのみ表示されます。パッケージ外部のコードでクラスを使用できるようにする場合、public でクラスを宣言する必要があります。例えば、次のパッケージには、SampleCode と CodeFormatter の 2 つのクラスが含まれています。

// SampleCode.as file 
package samples 
{ 
    public class SampleCode {} 
} 
 
// CodeFormatter.as file 
package samples 
{ 
    class CodeFormatter {} 
}

SampleCode クラスは、public クラスとして宣言されるので、パッケージの外部に表示されます。しかし、CodeFormatter クラスは、samples パッケージ内にのみ表示されます。 サンプルパッケージの外部にある CodeFormatter クラスにアクセスしようとすると、次の例に示すように、エラーが発生します。

import samples.SampleCode; 
import samples.CodeFormatter; 
var mySample:SampleCode = new SampleCode(); // okay, public class 
var myFormatter:CodeFormatter = new CodeFormatter(); // error

パッケージ外部で両方のクラスを使用する場合、両方のクラスを public として宣言する必要があります。パッケージ宣言に public 属性を適用することはできません。

完全修飾名は、パッケージを使用するときに発生する可能性がある名前のコンフリクトを解決する際に役立ちます。 例えば、同じ識別子でクラスを定義する 2 つのパッケージを読み込む場合などです。 例えば、SampleCode という名前のクラスも持つ次のパッケージがあるとします。

package langref.samples 
{ 
    public class SampleCode {} 
}

次に示すように両方のクラスを読み込むと、SampleCode クラスの使用時に名前の競合が発生します。

import samples.SampleCode; 
import langref.samples.SampleCode; 
var mySample:SampleCode = new SampleCode(); // name conflict

コンパイラーには、どちらの SampleCode クラスを使用するのかを知る方法はありません。 この競合を解決するには、次のように各クラスの完全修飾名を使用する必要があります。

var sample1:samples.SampleCode = new samples.SampleCode(); 
var sample2:langref.samples.SampleCode = new langref.samples.SampleCode();
注意: C++ を使用していたプログラマーは、import ステートメントと #include を混同しがちです。C++ コンパイラーは一度に 1 つのファイルを処理するため、C++ では #include ディレクティブが必要です。このディレクティブは、ヘッダーファイルが明示的に含まれていない限り、他のファイル内のクラス定義を探しません。ActionScript 3.0 には include ディレクティブがありますが、クラスやパッケージを読み込むように設計されていません。ActionScript 3.0 でクラスまたはパッケージを読み込むには、import ステートメントを使用して、パッケージを含むソースファイルをクラスパスに配置する必要があります。

名前空間

名前空間では、作成したプロパティおよびメソッドの可視性を制御することができます。 publicprivateprotected、および internal アクセス制御指定子は、ビルトイン名前空間のようなものです。これらのあらかじめ定義されているアクセス制御指定子が要件を満たさない場合は、独自の名前空間を作成することができます。

ActionScript 実装のシンタックスおよび詳細は XML とは少し異なりますが、XML 名前空間を使い慣れていれば、ここでの説明は新しいものではないかもしれません。 これまで名前空間を操作したことがない場合は、概念自体は単純ですが、その実装には特定の用語を習得する必要があります。

名前空間の操作を理解するには、プロパティまたはメソッドの名前に識別子と名前空間の 2 つの部分があることを認識することが役立ちます。 識別子は名前のようなものと考えることができます。 例えば、次のクラス定義の識別子は sampleGreetingsampleFunction() です。

class SampleCode 
{ 
    var sampleGreeting:String; 
    function sampleFunction () { 
        trace(sampleGreeting + " from sampleFunction()"); 
    } 
}

定義の前に名前空間属性がない場合、名前は常にデフォルトの internal 名前空間によって修飾されます。つまり、名前は同じパッケージの呼び出し元だけに表示されます。コンパイラーが strict モードに設定されている場合は、名前空間属性を変更せずに internal 名前空間がすべての識別子に適用されることを示す警告がコンパイラーから出されます。識別子をどこでも使用できるようにするには、識別子名の前に public 属性を付ける必要があります。前のコード例では、sampleGreetingsampleFunction() の両方とも internal の名前空間値を持ちます。

名前空間を使用するときは、次の 3 つの手順に従います。 最初に、namespace キーワードを使用して名前空間を定義する必要があります。例えば、次のコードは、version1 名前空間を定義します。

namespace version1;

次に、プロパティまたはメソッドの宣言でアクセス制御指定子の代わりに使用して、名前空間を適用します。 次の例では、myFunction() という関数を version1 名前空間に配置します。

version1 function myFunction() {}

最後に、名前空間を適用すると、use ディレクティブを使用するか、識別子の名前を名前空間で修飾して参照できるようになります。次の例では、use ディレクティブから myFunction() 関数を参照します。

use namespace version1; 
myFunction();

次の例に示すように、修飾名を使用して myFunction() 関数を参照することもできます。

version1::myFunction();

名前空間の定義

名前空間には、名前空間名とも呼ばれる URI(Uniform Resource Identifier)という値が含まれます。URI によって、名前空間定義を一意にすることができます。

次のいずれかの方法で名前空間定義を宣言して、名前空間を作成します。 XML 名前空間を定義するように、明示的な URI で名前空間を定義することも、URI を省略することもできます。 次の例では、URI を使用して名前空間を定義する方法を示します。

namespace flash_proxy = "http://www.adobe.com/flash/proxy";

URI は、その名前空間の一意の識別ストリングとして機能します。 次の例のように URI を省略した場合、コンパイラーは URI の代わりに一意の内部 ID ストリングを作成します。この内部 ID ストリングにはアクセスできません。

namespace flash_proxy;

名前空間を定義すると、URI の有無に関わらず、その名前空間は同じスコープ内で再定義することはできません。 同じスコープ内で以前に定義された名前空間を定義しようとすると、コンパイルエラーが発生します。

名前空間がパッケージまたはクラス内で定義された場合は、適切なアクセス制御指定子が使用されていなければ、名前空間はそのパッケージまたはクラスの外部にあるコードに対して表示されません。 例えば、次のコードは flash.utils パッケージ内で定義された flash_proxy 名前空間を示します。次の例で、アクセス制御指定子が存在しないことは、flash_proxy 名前空間が flash.utils パッケージ内のコードだけに表示され、パッケージ外のコードからは参照できないことを意味します。

package flash.utils 
{ 
    namespace flash_proxy; 
}

次のコードは、public 属性を使用して、flash_proxy 名前空間がパッケージ外のコードから参照できるようにします。

package flash.utils 
{ 
    public namespace flash_proxy; 
}

名前空間の適用

名前空間を適用するとは、名前空間に定義を配置することです。 名前空間に配置できる定義には、関数、変数、および定数があります。カスタム名前空間にクラスを配置することはできません。

例えば、public アクセス制御名前空間で宣言された関数があるとします。関数定義に public 属性を使用すると、関数はパブリックの名前空間に配置され、すべてのコードで利用できるようになります。名前空間を定義した後は、public 属性を使用する場合と同じように使用でき、カスタム名前空間を参照可能なコードでその名前空間定義を使用できるようになります。例えば、次の例に示すように、名前空間 example1 を定義すると、example1 を属性として使用して myFunction() というメソッドを追加できます。

namespace example1; 
class someClass 
{ 
    example1 myFunction() {} 
}

名前空間 example1 を属性として使用して myFunction() メソッドを宣言することは、メソッドが example1 名前空間に属することを意味します。

名前空間を適用する際には、次の点に注意してください。

  • 適用できる名前空間は宣言ごとに 1 つだけです。

  • 名前空間属性を一度に複数の定義に適用することはできません。 つまり、10 個の異なる関数に名前空間を適用する場合、10 個の異なる関数それぞれに名前空間を属性として追加する必要があります。

  • 名前空間とアクセス制御指定子は相互に排他的であるため、名前空間を適用すると、アクセス制御指定子を指定することはできません。 つまり、名前空間を適用すると、publicprivateprotected、または internal として関数またはプロパティを宣言することはできません。

名前空間の参照

publicprivateprotectedinternal などのアクセス制御名前空間で宣言されたメソッドまたはプロパティを使用するとき、名前空間を明示的に参照する必要はありません。このような特別な名前空間へのアクセスはコンテキストによって制御されるからです。 例えば、private 名前空間に配置された定義は、自動的に同じクラス内のコードで利用できるようになります。しかし、定義した名前空間では、こうした状況依存性は存在しません。 カスタム名前空間に配置したメソッドまたはプロパティを使用するには、その名前空間を参照する必要があります。

use namespace ディレクティブで名前空間を参照できます。また、名前修飾子(::)を使用して名前空間で名前を修飾できます。use namespace ディレクティブで名前空間を参照すると、修飾されていない識別子に名前空間を適用できるように、名前空間が開きます。例えば、example1 名前空間を定義している場合、use namespace example1 を使用して、その名前空間内の名前にアクセスすることができます。

use namespace example1; 
myFunction();

一度に複数の名前空間を開くことができます。 use namespace で名前空間を開くと、名前空間はそれ自体が開かれたコードブロック全体で開かれたままになります。名前空間を明示的に閉じることはできません。

しかし、複数の名前空間を開くと、名前のコンフリクトが起こりやすくなります。 名前空間を開かない場合は、メソッドまたはプロパティの名前を名前空間と名前修飾子で修飾すると、use namespace ディレクティブを使用する必要はありません。例えば、次のコードは、example1 名前空間で名前 myFunction() を修飾する方法を示します。

example1::myFunction();

名前空間の使用

ActionScript 3.0 の一部である flash.utils.Proxy クラス内に名前の競合を避けるために使用されている名前空間の実際の例を見つけることができます。Proxy クラスは、ActionScript 2.0 の Object.__resolve プロパティの代替クラスであり、エラーが発生する前に未定義のプロパティまたはメソッドへの参照を取得します。Proxy クラスのすべてのメソッドは、名前の競合を避けるため flash_proxy 名前空間に置かれます。

flash_proxy 名前空間の使用方法についての理解を深めるために、Proxy クラスの使用方法を理解する必要があります。Proxy クラスの機能は、Proxy クラスを継承するクラスでのみ使用できます。 つまり、オブジェクト上で Proxy クラスのメソッドを使用する場合、そのオブジェクトのクラス定義は Proxy クラスを拡張する必要があります。例えば、未定義のメソッドへの呼び出しの試みを取得する場合、Proxy クラスを拡張し、Proxy クラスの callProperty() メソッドをオーバーライドします。

名前空間の実装には通常、名前空間の定義、適用、および参照の 3 つの手順を実行します。 しかし、Proxy クラスのメソッドを明示的に呼び出していないため、flash_proxy 名前空間は定義および適用されるだけで、参照されません。ActionScript 3.0 では、flash_proxy 名前空間を定義し、これを Proxy クラス内に適用します。コードは、flash_proxy 名前空間を Proxy クラスを拡張するクラスに適用するだけです。

flash_proxy 名前空間は、次のような方法で flash.utils パッケージで定義されます。

package flash.utils 
{ 
    public namespace flash_proxy; 
}

次の Proxy クラスからの抜粋に示すように、名前空間が Proxy クラスのメソッドに適用されます。

public class Proxy 
{ 
    flash_proxy function callProperty(name:*, ... rest):* 
    flash_proxy function deleteProperty(name:*):Boolean 
    ... 
}

次のコードに示すように、まず Proxy クラスと flash_proxy 名前空間の両方を読み込む必要があります。次に、Proxy クラスを拡張するようにクラスを宣言する必要があります。strict モードでコンパイルしている場合は、dynamic 属性を追加する必要もあります。callProperty() メソッドをオーバーライドするときは、flash_proxy 名前空間を使用する必要があります。

package 
{ 
    import flash.utils.Proxy; 
    import flash.utils.flash_proxy; 
 
    dynamic class MyProxy extends Proxy 
    { 
        flash_proxy override function callProperty(name:*, ...rest):* 
        { 
            trace("method call intercepted: " + name); 
        } 
    } 
}

MyProxy クラスのインスタンスを作成し、次の例で呼び出される testing() メソッドなどの未定義のメソッドを呼び出すと、Proxy オブジェクトがメソッドの呼び出しを取得し、オーバーライドされた callProperty() メソッド内のステートメントを実行します(この場合は、単純な trace() ステートメント)。

var mySample:MyProxy = new MyProxy(); 
mySample.testing(); // method call intercepted: testing

flash_proxy 名前空間内に Proxy クラスのメソッドを配置すると、2 つの利点があります。1 つ目は、別の名前空間があると、Proxy クラスを拡張するクラスのパブリックインターフェイスを整理できるという点です。 Proxy クラスには、オーバーライドできるメソッドが 10 個以上ありますが、これらのメソッドは直接呼び出せるように設計されていません。 これらすべてをパブリック名前空間に配置すると、混乱の元になります。 2 つ目は、Proxy サブクラスに Proxy クラスのメソッドと一致する名前のインスタンスメソッドが含まれている場合、flash_proxy 名前空間を使用すると、名前のコンフリクトを回避できることです。例えば、独自のメソッド callProperty() に名前を付けるとします。この callProperty() メソッドは別の名前空間にあるため、次のコードは有効です。

dynamic class MyProxy extends Proxy 
{ 
    public function callProperty() {} 
    flash_proxy override function callProperty(name:*, ...rest):* 
    { 
        trace("method call intercepted: " + name); 
    } 
}

名前空間は、4 つのアクセス制御指定子(publicprivateinternal、および protected)では実行できない方法でメソッドまたはプロパティにアクセスする場合にも便利です。例えば、複数のパッケージに分散するいくつかのユーティリティメソッドがあるとします。 これらのメソッドをパブリックにしないで、すべてのパッケージで利用できるようにするには、名前空間を作成して、独自のアクセス制御指定子として使用します。

次の例では、ユーザー定義の名前空間を使用して、異なるパッケージにある 2 つの関数をグループ化します。これらを同じ名前空間にグループ化することにより、両方の関数を 1 つの use namespace ステートメントを介してクラスまたはパッケージに表示することができます。

この例では、4 つのファイルを使用してその手法を示します。 ファイルはすべてクラスパス内にある必要があります。 1 つ目のファイル myInternal.as を使用して myInternal 名前空間を定義します。このファイルは example パッケージにあるため、example という名前のフォルダーに配置する必要があります。 この名前空間は public とマークされているので、他のパッケージに読み込むことができます。

// myInternal.as in folder example 
package example 
{ 
    public namespace myInternal = "http://www.adobe.com/2006/actionscript/examples"; 
}

2 つ目と 3 つ目のファイル Utility.as と Helper.as は、他のパッケージで使用できるメソッドを含むクラスを定義します。 Utility クラスは example.alpha パッケージ内にあります。つまり、example フォルダーのサブフォルダーである alpha という名前のフォルダーにファイルを配置する必要があります。 Helper クラスは example.beta パッケージ内にあります。つまり、example フォルダーのサブフォルダーでもある beta という名前のフォルダーにファイルを配置する必要があります。 これらのパッケージ example.alpha と example.beta の両方とも名前空間を使用する前に読み込む必要があります。

// Utility.as in the example/alpha folder 
package example.alpha 
{ 
    import example.myInternal; 
     
    public class Utility 
    { 
        private static var _taskCounter:int = 0; 
     
        public static function someTask() 
        { 
            _taskCounter++; 
        } 
         
        myInternal static function get taskCounter():int 
        { 
            return _taskCounter; 
        } 
    } 
} 
 
// Helper.as in the example/beta folder 
package example.beta 
{ 
    import example.myInternal; 
     
    public class Helper 
    { 
        private static var _timeStamp:Date; 
         
        public static function someTask() 
        { 
            _timeStamp = new Date(); 
        } 
         
        myInternal static function get lastCalled():Date 
        { 
            return _timeStamp; 
        } 
    } 
}

4 つ目のファイル NamespaceUseCase.as は、メインアプリケーションクラスで、example フォルダーと兄弟関係になければなりません。 Flash Professional では、このクラスは FLA の document クラスとして使用されます。NamespaceUseCase クラスも myInternal 名前空間を読み込み、この名前空間を使用して、他のパッケージにある 2 つの静的メソッドを呼び出します。この例では、コードを簡略化するためにのみ静的メソッドを使用します。 静的メソッドおよびインスタンスメソッドは両方とも myInternal 名前空間に配置することができます。

// NamespaceUseCase.as 
package 
{ 
    import flash.display.MovieClip; 
    import example.myInternal; // import namespace 
    import example.alpha.Utility;// import Utility class 
    import example.beta.Helper;// import Helper class 
     
    public class NamespaceUseCase extends MovieClip 
    { 
        public function NamespaceUseCase() 
        { 
            use namespace myInternal; 
             
            Utility.someTask(); 
            Utility.someTask(); 
            trace(Utility.taskCounter); // 2 
             
            Helper.someTask(); 
            trace(Helper.lastCalled); // [time someTask() was last called] 
        } 
    } 
}