仕様と問題点
- JavaScriptにはアクセス修飾子はない。
- JavaScriptのスコープは、「グローバルスコープ」と「関数スコープ(ローカルスコープ)」のみ。JavaScriptではスコープ(変数の有効範囲)は関数単位(function(){~})になるのが基本。
- 一番外側のどの関数の中にも含まれないものは「グローバルスコープ」に所属する「グローバル変数」となり、どこからでも利用できる変数になる。
- JavaScriptには他の言語にあるようなブロックスコープがない。
- JavaScriptでは本来ファイルを分割して作ることを考慮しておらず、単純なファイルの結合のみ。グローバルでの汚染は複数のファイルを読み込んだときやファイルを結合したときに発生する。
対応
- 関数は任意にスコープを作るための唯一の手段
- ファイル全体を関数で括る事でスコープを生成して、ファイル内でグローバルではない共通の変数(クラス内のインスタンス変数のようなイメージ)を作成することが可能。しかし、var宣言なしだと、結局グローバル変数になってしまうので、必ず、var宣言が必要となる。
- 関数は任意にスコープを作るための唯一の手段であり、即時関数も関数ですのでスコープを提供する。
- 関数スコープの中でvarを使って定義された変数は関数の中でローカルな変数になるので、関数の外側の変数を上書きしたりすることはない。
即時関数
- 即時関数は、関数を定義すると同時に実行するための構文
- 再利用しない関数を実行する場合のコード量を減らすための手段
- 即時関数はスコープを汚染せずに新たなスコープを作成するための手段
1.任意にスコープを作成する
2.ファイル全体を即時関数で囲む
1 2 3 |
(function() { // … })(); |
- ファイル全体を即時関数に囲むことで、ファイルモジュールと同様の扱いになる。ファイル内(即時関数内)で定義されたvar宣言の変数は、自身以外のソースファイルや他のライブラリやに影響を与えない。
- 関数スコープの中でvarを使って定義された変数は、関数の中でローカルな変数になるので、関数の外側(この場合は他のファイル)の変数を上書きしたりすることはない。
- JavaScriptを複数のファイルに分割した時の、グローバル汚染に対応する。
1 2 3 4 5 6 7 8 |
(function(global) { "use strict"; var x = 0; function countA() { return x++; } global.countA = countA; })(this); |
1 2 3 4 5 6 7 8 |
(function(global) { "use strict"; var x = 0 function countB() { return x++; } global.countB = countB; })(this); |
- 即時関数の引数に唯一のアプリケーションのグローバルオブジェクトを渡すことで、グルーバル変数の範囲を限定して、グローバル汚染に対応する。
- 各ファイルのvar宣言のxは、各ファイル内で隠ぺいされる。
- countAとcountBの関数は、グローバルオブジェクトを介して呼び出す事が可能。
参考資料
JSファイル全体を括るfunctionについての話。(JavaScriptおれおれAdvent Calendar 2014 – 01日目)
JavaScriptで即時関数(function(){…})()を使う
3. ページの初期化
- ページ読み込み時に時刻等を初期表示する処理。一時変数は処理完了後には不要なので、ローカル変数として定義したい。
- 処理コード全体を即時関数で包むことで、ローカル変数を実現している。
1 2 3 4 5 6 |
//初期化処理 (function () { var label = document.getElementById('date_label'); var now = new Date(); label.innerText = now; }()); |
4.変数の初期化処理
- 変数を初期化する関数が一度のみしか使用されない場合、他から関数が使用されたくない。
1 2 3 4 5 6 7 8 |
var device = (function() { var ua = navigator.userAgent; if ((ua.indexOf('iPhone') > 0 && ua.indexOf('iPad') == -1) || ua.indexOf('iPod') > 0 || ua.indexOf('Android') > 0) { return 'sp'; } else { return 'pc'; } }()); |
クロージャ
- ここでは、スコープの話として説明する(JavaScriptは関数ごとにスコープが作られます。)
- クロージャとは「自分を囲むスコープにある変数を参照できる関数」の事で、内側の関数が外側の関数の変数を参照できる機能
1 2 3 4 5 6 7 8 9 10 11 |
function func() { var value = 1; function innerFunc() { console.log(value); } innerFunc(); } func(); // 1 func(); // 1 func(); // 1 |
内側の関数innerFuncが外側の関数funcの変数を参照できている。
Node.jsのスコープの検討
- 普通にファイル単位にモジュール概念が適用されるため、一番外側でどの関数の中にも含まれないものでも、グルーバルスコープにならない。
- export,requireで他のファイルから参照可能になる。Cのexternのイメージ。