メインコンテンツまでスキップ

変数のスコープ (scope)

スコープ(scope)とは、変数がどこから参照できるかを定めた変数の有効範囲のことです。JavaScriptには大きく分けてグローバルスコープとローカルスコープの2つがあります。

グローバルスコープ

グローバルスコープ(global scope)はプログラムのどこからでも参照できる変数です。JavaScriptにはグローバルオブジェクト(global object)と呼ばれるオブジェクトがたったひとつ存在します。ブラウザではwindowオブジェクトがグローバルオブジェクトです。

グローバル変数は、グローバルオブジェクトのプロパティになります。ブラウザでは、windowオブジェクトのプロパティになっていることになります。日付のDateクラスや、デバッグに使うconsoleオブジェクトなどの組み込みAPIはすべてwindowオブジェクトのプロパティです。グローバル変数へのアクセスはwindowを省略して書くことができます。

js
Date === window.Date; //=> true
console === window.console; //=> true
js
Date === window.Date; //=> true
console === window.console; //=> true

ローカルスコープ以外でvarを用いて変数宣言すると、グローバル変数になります。ただ、varの使用は本書としては非推奨です。

📄️ varはもう使わない

varは古い変数宣言の方法です。varにはいくつかの問題点がありました。それを解決するために、ES2015でletとconstが導入されました。ここでは、varとその問題点を説明します。新たにコードを書く場合にはvarは使わずにletとconstを使うことを推奨します。

ローカルスコープ

ローカルスコープ(local scope)は、一定範囲にだけ効く変数スコープです。

関数スコープ

関数スコープ(function scope)は、関数内でのみ参照できる範囲です。関数内で宣言された変数は、関数の外から参照できません。

js
function func() {
const variable = 123;
return variable; // 参照できる
}
console.log(variable); // 参照できない
js
function func() {
const variable = 123;
return variable; // 参照できる
}
console.log(variable); // 参照できない

レキシカルスコープ

レキシカルスコープ(lexical scope)変数とは、関数を定義した地点から参照できる、関数の外の変数を言います。

js
const x = 100;
 
function a() {
console.log(x); // 関数の外の変数が見える
}
 
a();
100
js
const x = 100;
 
function a() {
console.log(x); // 関数の外の変数が見える
}
 
a();
100

ブロックスコープ

ブロックスコープ(block scope)は、ブレース{ }で囲まれた範囲だけ有効なスコープです。ブロックスコープ内の変数は、ブロックの外から参照できません。

js
{
const x = 100;
console.log(x);
100
}
console.log(x); // xを参照できない
ReferenceError: x is not defined
js
{
const x = 100;
console.log(x);
100
}
console.log(x); // xを参照できない
ReferenceError: x is not defined

ブロックスコープはif構文などのブレースにも作用します。条件分岐の中で変数宣言された変数は、条件分岐の外からは参照できないので注意しましょう。

js
if (navigator.userAgent.includes("Firefox")) {
const browser = "Firefox";
} else {
const browser = "Firefox以外";
}
console.log(browser); // 参照できずエラー
js
if (navigator.userAgent.includes("Firefox")) {
const browser = "Firefox";
} else {
const browser = "Firefox以外";
}
console.log(browser); // 参照できずエラー

上の例は、ブロックスコープの外で変数宣言するように書き換える必要があります。

js
let browser;
if (navigator.userAgent.includes("Firefox")) {
browser = "Firefox";
} else {
browser = "Firefox以外";
}
console.log(browser); // OK
js
let browser;
if (navigator.userAgent.includes("Firefox")) {
browser = "Firefox";
} else {
browser = "Firefox以外";
}
console.log(browser); // OK

意図しないグローバル変数への代入

JavaScriptではローカルスコープの変数に代入したつもりが、グローバル変数に代入してしまっていたといった事故が起こりえます。ローカル変数を宣言する場合は、letconstを用いますが、これを書き忘れた変数代入は、グローバル変数になってしまいます。

js
function func() {
foo = "ローカル変数のつもり";
}
func();
console.log(window.foo);
"ローカル変数のつもり"
js
function func() {
foo = "ローカル変数のつもり";
}
func();
console.log(window.foo);
"ローカル変数のつもり"

JavaScriptで変数を扱う際は、誤ってグローバル変数を作ってしまわないよう注意が必要です。一方、TypeScriptでは変数宣言されていない変数に代入しようとすると、コンパイラが指摘してくれます。

ts
function func() {
foo = "ローカル変数のつもり";
Cannot find name 'foo'.2304Cannot find name 'foo'.
}
ts
function func() {
foo = "ローカル変数のつもり";
Cannot find name 'foo'.2304Cannot find name 'foo'.
}

意図しないグローバル変数への代入は、JavaScriptの残念な仕様と言えますが、TypeScriptを使っているとこういったトラブルも発見しやすくなります。

  • 質問する ─ 読んでも分からなかったこと、TypeScriptで分からないこと、お気軽にGitHubまで🙂
  • 問題を報告する ─ 文章やサンプルコードなどの誤植はお知らせください。