入門⑥

■スコープチェーン
スコープチェーンとは、Functionオブジェクトが実行される環境を定義するスコープ連鎖のこと。

変数の参照時には、このリストを末尾から先頭に向かって検索し、
参照できるプロパティが存在しない場合は例外を発生させ、
変数の更新時には、Globalオブジェクトにプロパティを作成します。


例1.
var a=1;
function test() { var b=2; }
test();

●↑の実行手順
1.Globalオブジェクトの初期化
  全ての変数宣言と関数定義と同名のプロパティがwindowオブジェクトに作成される。
  (a=undefined,test=undefined)

2.スクリプトが実行される。

3.a=1がGlobalオブジェクトに格納される

4.test()が定義される。

5.Function test()が実行され、Activationオブジェクトが初期化される。

6.testの中で使われる変数bを生成(b=undefined)し、引数はないので、
  argumentsプロパティには何も格納されない。

7.関数定義と同名のプロパティtest=test()が生成され、
  [[Scope]]プロパティにa=1とb=2が生成される。



例2.
var a = 1;
function test1() { var b = 2; test2(); }
function test2() { var c = 3; }
test1();


●↑の解説

1.Globalオブジェクトの初期化が行われる。
  全ての変数宣言と関数定義と同名のプロパティがwindowオブジェクトに作成される。
  (a=undefined,test1=undefined,test2=undefined)

2.スクリプトが実行される。

3.a=1がGlobalオブジェクトに格納される

4.test1()、test2()が定義される。

5.Function test1()が実行され、Activationオブジェクトが初期化される。

6.test1の中で使われる変数bと関数test2()が生成され、
  (b=undefined,test2()=undefined)
  関数定義と同名のプロパティtest1=test1()が生成され、
  [[Scope]]プロパティにa=1とb=2が生成される。

7.test2()が実行されると、Activationオブジェクトが生成され、
  test2の[[Scope]]プロパティの末尾にc=3が追加される。

※※この時、test1()とtest2()のスコープチェーンは別々であり、
  test1()の[[Scope]]のbはtest2()の[[Scope]]からは参照できない。


≪console出力して挙動をチェック≫
var a = 1;
function test1() {
var b = 2;
test2();
}
function test2() {
var c = 3;
console.log(test1); // test1()
console.log(test2); // test2()
console.log(a); // 1
console.log(c); // 3
console.log(b); //error
}
test1();


☆まとめ
Globalスコープに配置した変数はどこからでも参照できます。
基本的にはGlobalスコープに配置する変数は必要最小限に抑えると良いです。

また、Activationオブジェクトは関数を実行する度に作られるので、同じ変数名でも複数存在し得る。



クロージャ
スコープチェーンのように、引数以外の変数を実行時の環境ではなく、
自身が定義された環境(静的スコープ)において解決する関数のことをクロージャと呼びます。

ex.

function newCounter() {
var i = 0;
return function() { // 無名関数
i = i + 1;
return i;
}
}
c1 = newCounter();
c2 = newCounter();
alert(c1()); // 1
alert(c1()); // 2
alert(c1()); // 3
alert(c1()); // 4
alert(c1()); // 5
alert(c2()); // 6ではなく、1になる
alert(c2()); // 7ではなく、2になる

c1とc2が、それぞれ独立して値を保持します。


※遅延評価(呼び出されるまで実行されない)の性質を持つため、
一旦計算された値をキャッシュすることにより、遅延プロミスは最大で
一度しか計算されないようにすることができます。

※RubyやC#2.0には、既にある機能でJava7にも追加するという言語拡張が検討されいましたが
導入されないことになりました。



■Functionクラス
関数をFunctionクラスで生成すると、スコープチェーンではなく、
常にGlobalスコープの変数を参照するようになります。

ex.

var a = 1;
function test() {
var a = 2;
return Function('','return a');
}

console.log(test()()); //1
console.log(test()); //anonymous()

※Function()で作成した場合、Firefoxでは上記のようにanonymous(匿名)関数と表示され、
 Safariではfunction anonymous() {'','return a'; }のように、関数名なしで表示されます。

 実際には、anonymous()という関数名は存在せず、
 匿名関数/無名関数はイベントハンドラの設定や、
 関数名を重複させたくない場合などに応用することができます。



■with文
with文は指定したオブジェクトをスコープチェーンに追加します。
そのため、次のようにオブジェクトを指定することなくプロパティを参照できます。

ex.
function math1(){
document.write(Math.ceil(10.5)); // 小数点切り上げの値を表示
document.write(Math.PI); // 円周率πを表示
}

function math2(){
with(Math) {
document.write(ceil(10.5)); //小数点切り上げの値を表示
document.write(PI); // 円周率πを表示
}
}
math1();//113.141592653589793
math2();//113.141592653589793