JavaScript 第5版 - 8章 関数(3)

JavaScript 第5版、「8章 関数」の「8.8 関数のスコープとクロージャ」~「8.9 Function()コンストラクタ」(140~149ページ)を読み進めていきます。

8.8 関数のスコープとクロージャ

8.8.1 静的なスコープ

  • JavaScriptのスコープは静的なものであり、関数の実行スコープは、実行時のものではなく定義時のものとなる。

8.8.2 Callオブジェクト

  • 関数が呼び出されると、次のような順番でスコープが設定される。
    1. 関数定義時のスコープチェーンを設定。
    2. Callオブジェクト(ECMAScriptではactivationオブジェクトと呼ばれる)をスコープチェーンの先頭に追加。
    3. Callオブジェクトの初期化時に、Argumentsオブジェクトを参照するargumentsプロパティを生成。
    4. 関数の名前付き引数、var文で宣言されたローカル変数をCallオブジェクトに追加。

8.8.4 クロージャとしての入れ子型の関数

入れ子型の関数を含む関数がある場合、その関数の呼び出しによって新たに生成されたCallオブジェクトは、関数の引数やローカル変数、入れ子型の関数などを参照します。そして、入れ子型の関数の生成時には、スコープチェーンに外側の関数のCallオブジェクトが含まれていることから、入れ子型の関数から外側の関数のCallオブジェクト(つまり、外側の関数の引数やローカル変数など)を参照することができます。

関数が入れ子型の関数を複数持つ場合は、入れ子型の関数は同じCallオブジェクトを共有します。

通常は、外側の関数の実行が終了すると、入れ子型の関数のスコープと外側の関数のスコープは消滅します。しかし、入れ子型の関数が、関数の定義されたスコープ外から参照されるような場合、入れ子型の関数と外側の関数のスコープは保持され続けます。

以下はクロージャを使ったコードの例です。

function makefunc(x) {
    return function() { return x; }
}

var a = [makefunc(0), makefunc(1), makefunc(2)];

alert(a[0]());  // 0が表示される
alert(a[1]());  // 1が表示される
alert(a[2]());  // 2が表示される

この例では、makefunc()関数によって返された(入れ子型の)関数を配列に格納することで、makefunc()関数が終了した後にも入れ子型の関数への参照が存在することになります。つまり、入れ子型の関数と外側の関数のスコープは保持され続けます(Callオブジェクトは関数の呼び出しごとに新たに生成されるため、保持されるのもそれぞれ異なるスコープです)。

配列に格納された関数を呼び出した際には、スコープチェーンをたどって(保持されている入れ子型の関数のスコープ→保持されているmakefunc()関数のスコープ)、makefunc()関数に渡された引数のxがそれぞれ参照され、その値が返されていることになります。

8.8.4.1 クロージャの例

144~145ページに、クロージャを使った例として、関数内からしかアクセスできないプライベートプロパティを実現する方法が掲載されています。また、146~147ページには、Steve Yen氏のブレークポイントシステムを参考にした、クロージャを使ったブレイクポイント関数が掲載されています。

8.8.4.2 Internet Explorerにおけるクロージャとメモリリーク

IEでは、参照が循環する場合にガーベジコレクションされない問題があり、クロージャを使った場合に循環参照によるメモリリークが発生しやすいとのことです。「Understanding and Solving Internet Explorer Leak Patterns」でメモリリークが発生するパターンが解説されていますが、高度すぎて今は理解できていません :x

8.9 Function()コンストラクタ

  • Function()コンストラクタは、引数に渡された文字列を元に匿名関数を生成する。
  • Function()コンストラクタには次のような特徴がある。
    • JavaScriptのコードを実行時に動的に生成してコンパイルできる。
    • Function()コンストラクタを使うたびに、関数の本体が解釈され、新しい関数オブジェクトが生成される。つまり、効率は非常に悪い。
    • Function()コンストラクタが生成する関数は、静的スコープを使わず、常にトップレベルの関数としてコンパイルされる。
var f = new Function('x', 'y', 'return x * y;');
// function文で記述した次のコードとほぼ同じ。
// function f(x, y) { return x * y; }

これで8章は読了です。この8章の後半辺りからJavaScriptのおもしろさがどんどん出てくるのですが、それと同時に内容も高度になってくるため、8章以降はまだまだ何度も読み返す必要がありそうです。

コメント (0)

この記事へのコメントはまだありません。

コメントフォーム

トラックバック (1)

[JavaScript]JavaScriptを学ぶ上で避けては通れないレキシカルスコープ

JavaScriptを学習する上で最も難解ではないかと思うのが、レキシカルスコープ(静的スコープ)とそれを利用したクロージャという仕組みです。両者のうち、レキシカルスコープだけは、な…

この記事のトラックバックURI
http://dxd8.com/archives/75/trackback/
この記事のURI
http://dxd8.com/archives/75/