変数のスコープ (scope)
スコープ(scope)とは、変数がどこから参照できるかを定めた変数の有効範囲のことです。JavaScriptには大きく分けてグローバルスコープとローカルスコープの2つがあります。
#
グローバルスコープグローバルスコープ(global scope)はプログラムのどこからでも参照できる変数です。JavaScriptにはグローバルオブジェクト(global object)と呼ばれるオブジェクトがたったひとつ存在します。ブラウザではwindow
オブジェクトがグローバルオブジェクトです。
グローバル変数は、グローバルオブジェクトのプロパティになります。ブラウザでは、window
オブジェクトのプロパティになっていることになります。日付のDate
クラスや、デバッグに使うconsole
オブジェクトなどの組み込みAPIはすべてwindow
オブジェクトのプロパティです。グローバル変数へのアクセスはwindowを省略して書くことができます。
javascript
Date === window.Date; //=> trueconsole === window.console; //=> true
javascript
Date === window.Date; //=> trueconsole === window.console; //=> true
ローカルスコープ以外でvar
を用いて変数宣言すると、グローバル変数になります。ただ、var
の使用は本書としては非推奨です。
#
ローカルスコープローカルスコープ(local scope)は、一定範囲にだけ効く変数スコープです。
#
関数スコープ関数スコープ(function scope)は、関数内でのみ参照できる範囲です。関数内で宣言された変数は、関数の外から参照できません。
javascript
function func() {const variable = 123;return variable; // 参照できる}console.log(variable); // 参照できない
javascript
function func() {const variable = 123;return variable; // 参照できる}console.log(variable); // 参照できない
#
レキシカルスコープレキシカルスコープ(lexical scope)変数とは、関数を定義した地点から参照できる、関数の外の変数を言います。
javascript
const x = 100;function a() {console.log(x); // 関数の外の変数が見える}a(); //=> 100
javascript
const x = 100;function a() {console.log(x); // 関数の外の変数が見える}a(); //=> 100
#
ブロックスコープブロックスコープ(block scope)は、ブレース{ }
で囲まれた範囲だけ有効なスコープです。ブロックスコープ内の変数は、ブロックの外から参照できません。
javascript
{const x = 100;console.log(x); //=> 100}console.log(x); // xを参照できない// エラー: ReferenceError: x is not defined
javascript
{const x = 100;console.log(x); //=> 100}console.log(x); // xを参照できない// エラー: ReferenceError: x is not defined
ブロックスコープはif構文などのブレースにも作用します。条件分岐の中で変数宣言された変数は、条件分岐の外からは参照できないので注意しましょう。
javascript
if (navigator.userAgent.includes("Firefox")) {const browser = "Firefox";} else {const browser = "Firefox以外";}console.log(browser); // 参照できずエラー
javascript
if (navigator.userAgent.includes("Firefox")) {const browser = "Firefox";} else {const browser = "Firefox以外";}console.log(browser); // 参照できずエラー
上の例は、ブロックスコープの外で変数宣言するように書き換える必要があります。
javascript
let browser;if (navigator.userAgent.includes("Firefox")) {browser = "Firefox";} else {browser = "Firefox以外";}console.log(browser); // OK
javascript
let browser;if (navigator.userAgent.includes("Firefox")) {browser = "Firefox";} else {browser = "Firefox以外";}console.log(browser); // OK
#
意図しないグローバル変数への代入JavaScriptではローカルスコープの変数に代入したつもりが、グローバル変数に代入してしまっていたといった事故が起こりえます。ローカル変数を宣言する場合は、let
やconst
を用いますが、これを書き忘れた変数代入は、グローバル変数になってしまいます。
javascript
function func() {foo = "ローカル変数のつもり";}func();console.log(window.foo); //=> "ローカル変数のつもり"
javascript
function func() {foo = "ローカル変数のつもり";}func();console.log(window.foo); //=> "ローカル変数のつもり"
JavaScriptで変数を扱う際は、誤ってグローバル変数を作ってしまわないよう注意が必要です。一方、TypeScriptでは変数宣言されていない変数に代入しようとすると、コンパイラが指摘してくれます。
typescript
function func() {foo = "ローカル変数のつもり";// コンパイルエラー: Cannot find name 'foo'.(2304)}
typescript
function func() {foo = "ローカル変数のつもり";// コンパイルエラー: Cannot find name 'foo'.(2304)}
意図しないグローバル変数への代入は、JavaScriptの残念な仕様と言えますが、TypeScriptを使っているとこういったトラブルも発見しやすくなります。