JavaScriptの変数スコープ(グローバル・ローカル・ブロック)についてまとめました。
目次
1. 変数のスコープ(概要)
JavaScriptの変数スコープは、以下の3つに分けられます。
- グローバルスコープ
- ローカルスコープ
- ブロックスコープ(ES2015)
各スコープを持つ変数の特徴は、以下となります。
グローバル変数
- スクリプト全体(グローバルスコープ)から参照できる変数。
- 関数の外で定義した変数(例1)、もしくは var をつけないで宣言した変数(例2)がグローバル変数になります。
ローカル変数
- 関数内(ローカルスコープ)でのみ参照できる変数。
- 関数内で var を付けて宣言した変数が、ローカル変数になります。
- 関数内であっても var を付けずに宣言すると、グローバル変数になります。
ブロック変数
- ブロック “{ }” で括られた範囲でのみ参照できる変数。
- ブロック内で let 宣言された変数がブロッック変数になります。
- ES2015に対応したブラウザで使用できます。
2. 変数のスコープ(詳細)
実際のコードで、それぞれの動作を確認します。
グローバル変数
例1:関数の外で定義した変数
- ① でグローバル変数(scope1) を宣言しています。
- ② で同名の変数を getScope1関数内で宣言しています。
- ③ でコンソールに出力される文字は、① の変数に代入した値です。
1 2 3 4 5 6 7 8 9 |
var scope1 = 'グローバル変数'; // ① function getScope1(){ var scope1 = 'ローカル変数'; // ② ローカル変数 return scope1; } console.log(getScope1()); // ローカル変数 console.log(scope1); // ③ グローバル変数 |
① と ② は同名の変数ですが、② はローカルスコープの変数のため、① の値を上書きしません。
例2: var をつけないで宣言した変数
- ① で var を使用せず変数(scope1)を宣言しています。
- ③ では、関数の外から直接 scope1 にアクセスしていますが、エラーにならず “グローバル変数” と表示されます。
1 2 3 4 5 6 7 |
function getScope1(){ scope1 = 'グローバル変数'; // ① グローバル変数 return scope1; } console.log(getScope1()); // ② グローバル変数 console.log(scope1); // ③ グローバル変数 |
このように、関数内であっても、var を使用せず宣言した変数は、グローバル変数になります。
JavaScriptでは、変数宣言に var は必須ではありませんが、このようにスコープの範囲が分かりづらくなるため、必ず var をつけるようにした方がいいでしょう。
ローカル変数
例:ローカル変数の宣言
- ① でローカル変数(scope1)を宣言しています。
- ② では、”ローカル変数” と出力されます。
- ③ では、ローカル変数の値を外部から参照しようとしているためエラーになります。(not defined)
1 2 3 4 5 6 7 |
function getScope1(){ var scope1 = 'ローカル変数'; // ① return scope1; } console.log(getScope1()); // ② ローカル変数 console.log(scope1); // ③ エラーになる(not defined) |
ブロック変数
例:ブロック変数の宣言
- ① で let を使用して変数を定義しているため、ブロックスコープの変数を宣言しています。
④ では、ブロック外から、ブロック変数(block_scope)にアクセスしているため、エラーになります。 - ② は var で宣言しているため、ブロック変数にはなりません。(このブロックが関数外にあればグローバル変数に、関数内にあればローカル変数として扱われます)。
そのため、③から参照できエラーにはなりません。
1 2 3 4 5 6 7 8 9 |
{ let block_scope = 'ブロック変数'; // ① var other_scope = 'ブロックスコープにはならない'; // ② console.log(block_scope); // "ブロック変数"と表示される } console.log(other_scope); // ③ console.log(block_scope); // ④ エラーになる(not defined) |
上記のように、ブロック内でも let 宣言していない変数は、ブロック変数にならないので注意が必要です。
また、ES2015に対応していない環境で、ブロックスコープを実現したい場合は、即時関数を使用します。
例:”ブロック変数の宣言”を即時関数を使用して書いた場合
- スコープの範囲を匿名関数として定義して、callメソッドで呼び出します。
1 2 3 4 5 6 7 8 9 |
(function() { var block_scope = 'ブロック変数'; // ① var other_scope = 'ブロックスコープにはならない'; // ② console.log(block_scope); // "ブロック変数"と表示される }).call(this); console.log(other_scope); // ③ エラーになる(not defined) console.log(block_scope); // ④ エラーになる(not defined) |
この場合、即時関数内の var を使用して宣言した変数は、全てブロックスコープになります。
即時関数内でも、varを省略した場合は、グローバル変数になるため注意が必要です。
3. まとめ
各スコープについてまとめました。
ローカル、グローバル、ブロック関数がありますが、var を使用しないで定義するとグローバルスコープ扱いになるため注意が必要です。
JavaScriptの仕様では、var は必須ではありませんが、ソースの可読性を高め不具合を少なくするという意味では、var の使用は必須と思います。(ES2015対応環境では、let)
ちなみに、let の由来は stackoverflow に記載がありますので、興味のある方は参照してみてください。