ちょっと詳しくJavaScript 第4回【スコープとクロージャ】
2011年07月22日




                           株式会社ランチェスター
                           TEL: 03-5775-3395 Fax:03-5775-3396
                           URL: http://www.lanches.co.jp/
JavaScriptの変数とデータ型
        数値型(number)                              参照型(参照渡し)
        文字列型(string)
基本型
        真偽型(boolean)                             var a = {val:1};
        特殊(null、undefined)                       var b = a;
        オブジェクト(object)                           b.val = 5;
参照型     関数(function)                             alert(a.val); // 5
        配列(object)

基本型(値渡し)                                         var a = [1, 2, 3];
                                                 var b = a;
var a = 5;                                       b[0] = 5;
var b = a;                                       alert(a[0]); // 5
b = 10;
alert(a);     // 5                               ※ 関数の参照渡しは実例を見せる
                                                   のが難しいのでパス (´・ω・`)

                       Proprietary and Confidential to Lanchester Co.,LTD.   Page 1
JavaScriptの変数スコープ
• グローバルスコープ
   • プログラム中のどこからでも参照可能

• ローカルスコープ
   • その変数が宣言された関数の中でのみ参照可能

この2つしかないです。




              Proprietary and Confidential to Lanchester Co.,LTD.   Page 2
JavaScriptの変数スコープ - 実例1
var n = 1;           // グローバル

function f() {       // ←実はこれもグローバル
        var n = 0;   // ローカル
        return n;
}

alert(n);            // 1
alert(f());          // 0
alert(n);            // 1




                     Proprietary and Confidential to Lanchester Co.,LTD.   Page 3
JavaScriptの変数スコープ - 実例2
n = 1;              // グローバル

function f() {      // ←実はこれもグローバル
        n = 0;      // グローバル
        return n;
}

alert(n);           // 1
alert(f());         // 0
alert(n);           // 0

関数内で「var」を付けた変数がローカルスコープとなる。
グローバル変数の宣言は「var」を付けても付けなくても変わらない。


                    Proprietary and Confidential to Lanchester Co.,LTD.   Page 4
ローカルスコープは関数全体で有効
function f() {
        for(var i=0; i<10; i++) {
                // 特に何もしないよ
        }
        alert(i);      // ※
}

f();

※で「10」が表示される。

JavaScriptに「ブロックスコープ」は存在しない。



                    Proprietary and Confidential to Lanchester Co.,LTD.   Page 5
関数名も変数なので
function f() {
        g();                   // FunctionDeclarationにより
        function g() {         // 先に実行式があっても問題ない
                alert("localFunc");
        }
}

f();         // localFunc と表示
g();         // スコープ外なので実行不可

今までの考え方をそのまま活用でき、その通りの結果となる。




                   Proprietary and Confidential to Lanchester Co.,LTD.   Page 6
実は違った関数リテラルとFunction型
var n = 1;

function f() {
        var n = 0;
        var s1 = new Function("", "alert(n);");
        var s2 = function() { alert(n); };
        s1(); // 1→グローバルを参照
        s2(); // 0→ローカルを参照
}
f();

ECMAScriptの仕様により、Functionコンストラクタで作られる関数はグ
ローバルオブジェクトのスコープを参照して実行される。


                      Proprietary and Confidential to Lanchester Co.,LTD.   Page 7
実は違った関数リテラルとFunction型 - 2
// var n = 1;

function f() {
        var n = 0;
        var s = new Function("", "alert(n);");                              // エラー
}
f();

グローバル変数nがないためそもそも定義しようとした時点でエラーとな
ってしまう。




                      Proprietary and Confidential to Lanchester Co.,LTD.            Page 8
仮引数もローカルスコープ
var n = 1;

function f(n) {
        n++;
        return n;
}

alert(f(10));       // 11
alert(n);           // 1

同じ変数名だけど値渡しなのでグローバルのnはそのまんま。




                    Proprietary and Confidential to Lanchester Co.,LTD.   Page 9
仮引数もローカルスコープだけど・・・参照型
var a = [1, 2, 3];

function f(v) {
        v[0] = 10;
        return v;
}

alert(f(a));         // 10, 2, 3
alert(a);            // 10, 2, 3

違う変数名だけど参照渡しなので実体が変更される。




                     Proprietary and Confidential to Lanchester Co.,LTD.   Page 10
スコープチェイン
var a = 1;

function f() {
        var b = 2;
        function g() {
                var c = 3;
                return a + b + c;
        }
        return g();
}

alert(f());    // 6

自分自身のスコープにない変数の場合スコープを順に辿っていき、変
数を探し出す。
                       Proprietary and Confidential to Lanchester Co.,LTD.   Page 11
ところでthisって
<input type="button" value="test" id="btn">

<script type="text/javascript">
function where() {
          alert(this);
}

var o = { method: where };
document.getElementById("btn").onclick = where;

where();                     // [object Window]
o.method();                  // [object Object]
// [test]ボタンクリック             // [object HTMLInputElement]
</script>


「誰の関数として実行されたか」
がthisに入ってくる。
特にイベントハンドラの場合には注意。
                             Proprietary and Confidential to Lanchester Co.,LTD.   Page 12
本命:クロージャ(Closure、閉包)
• 引数以外の変数を実行時の環境ではなく、自身が定義された環境(
  静的スコープ)において解決することを特徴とする
   • Wikipediaより

• ローカル変数を参照している関数内関数
   • @ITより

• 関数自身が定義された環境を、ローカル変数も含めて持ち運ぶことの
  できる仕組み(またはそうした関数自体)
   • ZDNet builderより


・・・何言ってんだか分かんねっす(´・ω・`)


            Proprietary and Confidential to Lanchester Co.,LTD.   Page 13
シンプルなクロージャ
function closure(init) {
          var c = init;
          var f = function() {
                     c++;
                     return c;
          };
          return f;
}

var counter = closure(5);

alert(counter());   // 6
alert(counter());   // 7
alert(counter());   // 8


return fにより返された関数(counter)は、それが生成されたタイミング
でローカルスコープだったcを閉じ込めて(Closure/閉包)いる。


                             Proprietary and Confidential to Lanchester Co.,LTD.   Page 14
1つのクロージャを複数で使ってみる
function closure(init) {
          var c = init;
          return function(){ return ++c; }
}

var c1 = closure(5);
var c2 = closure(10);

alert(c1());       //   6
alert(c1());       //   7
alert(c2());       //   11
alert(c2());       //   12
alert(c1());       //   8
alert(c2());       //   13


それぞれがclosureを実行したタイミングでクロージャが作られている。



                             Proprietary and Confidential to Lanchester Co.,LTD.   Page 15
総決算
<input type="button" id="btn" value="設定">

<script type="text/javascript">
document.getElementById("btn").onclick = buttonState(true);

function buttonState(flag) {
        return function() {
                flag = !flag;
                this.value = flag ? "設定" : "解除";
        };
}
</script>



                     Proprietary and Confidential to Lanchester Co.,LTD.   Page 16
株式会社ランチェスター
TEL: 03-5775-3395 Fax:03-5775-3396
URL: http://www.lanches.co.jp/

ちょっと詳しくJavaScript 第4回【スコープとクロージャ】

  • 1.
    ちょっと詳しくJavaScript 第4回【スコープとクロージャ】 2011年07月22日 株式会社ランチェスター TEL: 03-5775-3395 Fax:03-5775-3396 URL: http://www.lanches.co.jp/
  • 2.
    JavaScriptの変数とデータ型 数値型(number) 参照型(参照渡し) 文字列型(string) 基本型 真偽型(boolean) var a = {val:1}; 特殊(null、undefined) var b = a; オブジェクト(object) b.val = 5; 参照型 関数(function) alert(a.val); // 5 配列(object) 基本型(値渡し) var a = [1, 2, 3]; var b = a; var a = 5; b[0] = 5; var b = a; alert(a[0]); // 5 b = 10; alert(a); // 5 ※ 関数の参照渡しは実例を見せる のが難しいのでパス (´・ω・`) Proprietary and Confidential to Lanchester Co.,LTD. Page 1
  • 3.
    JavaScriptの変数スコープ • グローバルスコープ • プログラム中のどこからでも参照可能 • ローカルスコープ • その変数が宣言された関数の中でのみ参照可能 この2つしかないです。 Proprietary and Confidential to Lanchester Co.,LTD. Page 2
  • 4.
    JavaScriptの変数スコープ - 実例1 varn = 1; // グローバル function f() { // ←実はこれもグローバル var n = 0; // ローカル return n; } alert(n); // 1 alert(f()); // 0 alert(n); // 1 Proprietary and Confidential to Lanchester Co.,LTD. Page 3
  • 5.
    JavaScriptの変数スコープ - 実例2 n= 1; // グローバル function f() { // ←実はこれもグローバル n = 0; // グローバル return n; } alert(n); // 1 alert(f()); // 0 alert(n); // 0 関数内で「var」を付けた変数がローカルスコープとなる。 グローバル変数の宣言は「var」を付けても付けなくても変わらない。 Proprietary and Confidential to Lanchester Co.,LTD. Page 4
  • 6.
    ローカルスコープは関数全体で有効 function f() { for(var i=0; i<10; i++) { // 特に何もしないよ } alert(i); // ※ } f(); ※で「10」が表示される。 JavaScriptに「ブロックスコープ」は存在しない。 Proprietary and Confidential to Lanchester Co.,LTD. Page 5
  • 7.
    関数名も変数なので function f() { g(); // FunctionDeclarationにより function g() { // 先に実行式があっても問題ない alert("localFunc"); } } f(); // localFunc と表示 g(); // スコープ外なので実行不可 今までの考え方をそのまま活用でき、その通りの結果となる。 Proprietary and Confidential to Lanchester Co.,LTD. Page 6
  • 8.
    実は違った関数リテラルとFunction型 var n =1; function f() { var n = 0; var s1 = new Function("", "alert(n);"); var s2 = function() { alert(n); }; s1(); // 1→グローバルを参照 s2(); // 0→ローカルを参照 } f(); ECMAScriptの仕様により、Functionコンストラクタで作られる関数はグ ローバルオブジェクトのスコープを参照して実行される。 Proprietary and Confidential to Lanchester Co.,LTD. Page 7
  • 9.
    実は違った関数リテラルとFunction型 - 2 //var n = 1; function f() { var n = 0; var s = new Function("", "alert(n);"); // エラー } f(); グローバル変数nがないためそもそも定義しようとした時点でエラーとな ってしまう。 Proprietary and Confidential to Lanchester Co.,LTD. Page 8
  • 10.
    仮引数もローカルスコープ var n =1; function f(n) { n++; return n; } alert(f(10)); // 11 alert(n); // 1 同じ変数名だけど値渡しなのでグローバルのnはそのまんま。 Proprietary and Confidential to Lanchester Co.,LTD. Page 9
  • 11.
    仮引数もローカルスコープだけど・・・参照型 var a =[1, 2, 3]; function f(v) { v[0] = 10; return v; } alert(f(a)); // 10, 2, 3 alert(a); // 10, 2, 3 違う変数名だけど参照渡しなので実体が変更される。 Proprietary and Confidential to Lanchester Co.,LTD. Page 10
  • 12.
    スコープチェイン var a =1; function f() { var b = 2; function g() { var c = 3; return a + b + c; } return g(); } alert(f()); // 6 自分自身のスコープにない変数の場合スコープを順に辿っていき、変 数を探し出す。 Proprietary and Confidential to Lanchester Co.,LTD. Page 11
  • 13.
    ところでthisって <input type="button" value="test"id="btn"> <script type="text/javascript"> function where() { alert(this); } var o = { method: where }; document.getElementById("btn").onclick = where; where(); // [object Window] o.method(); // [object Object] // [test]ボタンクリック // [object HTMLInputElement] </script> 「誰の関数として実行されたか」 がthisに入ってくる。 特にイベントハンドラの場合には注意。 Proprietary and Confidential to Lanchester Co.,LTD. Page 12
  • 14.
    本命:クロージャ(Closure、閉包) • 引数以外の変数を実行時の環境ではなく、自身が定義された環境( 静的スコープ)において解決することを特徴とする • Wikipediaより • ローカル変数を参照している関数内関数 • @ITより • 関数自身が定義された環境を、ローカル変数も含めて持ち運ぶことの できる仕組み(またはそうした関数自体) • ZDNet builderより ・・・何言ってんだか分かんねっす(´・ω・`) Proprietary and Confidential to Lanchester Co.,LTD. Page 13
  • 15.
    シンプルなクロージャ function closure(init) { var c = init; var f = function() { c++; return c; }; return f; } var counter = closure(5); alert(counter()); // 6 alert(counter()); // 7 alert(counter()); // 8 return fにより返された関数(counter)は、それが生成されたタイミング でローカルスコープだったcを閉じ込めて(Closure/閉包)いる。 Proprietary and Confidential to Lanchester Co.,LTD. Page 14
  • 16.
    1つのクロージャを複数で使ってみる function closure(init) { var c = init; return function(){ return ++c; } } var c1 = closure(5); var c2 = closure(10); alert(c1()); // 6 alert(c1()); // 7 alert(c2()); // 11 alert(c2()); // 12 alert(c1()); // 8 alert(c2()); // 13 それぞれがclosureを実行したタイミングでクロージャが作られている。 Proprietary and Confidential to Lanchester Co.,LTD. Page 15
  • 17.
    総決算 <input type="button" id="btn"value="設定"> <script type="text/javascript"> document.getElementById("btn").onclick = buttonState(true); function buttonState(flag) { return function() { flag = !flag; this.value = flag ? "設定" : "解除"; }; } </script> Proprietary and Confidential to Lanchester Co.,LTD. Page 16
  • 18.