プログラミングにおける「スコープ」とは、有効範囲のことで、スコープの対象となるのは主に変数、定数、関数です。
スコープはまずグローバルスコープとローカルスコープに大別され、ローカルスコープはさらに関数スコープとブロックスコープに分けられます。
- グローバルスコープ:ファイル内のどこからでも参照できる
- ローカルスコープ
- 関数スコープ:関数の中でのみ参照できる
- ブロックスコープ:波カッコで囲まれたブロックの中でのみ参照できる
目次
変数と定数のスコープ
JavaScriptの変数宣言に使用するキーワードは、2種類存在します。
let と var ですね。
let だけであれば話は単純なのですが、var が絡んでくると少々ややこしくなります。
一言で違いを表現すると、varで宣言された変数には、ブロックスコープがありません。
グローバルスコープ
関数やブロックに囲まれていない場所で宣言した変数(let宣言)および定数は、グローバルスコープを持ち、ファイルのどこからでも参照できます。
仮にそれがHTMLファイル内に書かれた別々の scriptタグであってもです。
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<title>スコープ</title>
</head>
<body>
<script>
// 関数にもブロックにも囲まれていない場所で宣言された変数 n
let n = 10;
</script>
<script>
// 別のscriptタグからでも参照できる
console.log(n);
</script>
</body>
</html>
> 10
関数スコープ
関数の中で宣言した変数(let宣言)および定数は、関数スコープを持ち、関数内からのみ参照できます。
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<title>スコープ</title>
</head>
<body>
<script>
function test() {
let n = 10;
console.log(`関数内の変数nは${n}です`);
}
test();
console.log(`関数の外から見た変数nは${n}です`);
</script>
</body>
</html>
> 関数内の変数nは10です
▶ Uncaught ReferenceError: n is not defined
ブロックスコープ
ブロックの中で宣言した変数(let宣言)および定数は、ブロックスコープを持ち、ブロック内からのみ参照できます。
JavaScriptのブロックは、基本的には波カッコ(中カッコ)に囲まれている領域で、ifブロックやforブロックなどがあります。
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<title>スコープ</title>
</head>
<body>
<script>
if ( 1 == 1 ) {
let n = 10;
console.log(`ifブロック内の変数nは${n}です`);
}
console.log(`ifブロックの外から見た変数nは${n}です`);
</script>
</body>
</html>
> ifブロック内の変数nは10です
▶ Uncaught ReferenceError: n is not defined
var 宣言された変数の場合
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<title>スコープ</title>
</head>
<body>
<script>
if ( 1 == 1 ) {
var n = 10;
console.log(`ifブロック内の変数nは${n}です`);
}
console.log(`ifブロックの外から見た変数nは${n}です`);
</script>
</body>
</html>
> ifブロック内の変数nは10です
> ifブロックの外から見た変数nは10です
👆このように、varによって宣言された変数は、ブロックスコープを完全に無視する挙動になっています。
var 宣言の問題点
スコープからは話題が逸れてしまいますが、ブロックスコープの概念が存在しない点以外にも、var宣言には重大な問題があります。
- 同名の変数を宣言できてしまう
- グローバル変数が上書きされるリスクがある
同名の変数を宣言できてしまう
var n = 1;
var n = 2;
console.log(n);
👆このようなプログラムがエラーも発生せず動作してしまいます。
これでは人為的ミスを未然に防げなくなる確率は増大しますよね。
グローバル変数が上書きされるリスクがある
JavaScriptには、windowというオブジェクトが組み込まれています。
var宣言された変数は、そのwindowオブジェクトのプロパティとして追加されるため、windowが元々持っているプロパティと同じ名前の変数を宣言すると、元のプロパティは上書きされてしまいます。
例えば、windowオブジェクトは元々「name」というプロパティを持っています。
確認してみましょう。
console.log(window);
▼ window
...途中省略
▶ name: ""
そして、varを使って name という変数名で宣言してしまうと…恐ろしいことが起こります。
var name = "taro";
console.log(window.name);
> taro
windowのプロパティであるnameが「taro」に書き換えられてしまいました。
他にも「変数の巻き上げ」という問題もありますが、ここでは取り上げません。
これからJavaScriptでプログラミングを始める際には、くれぐれもvarは使わず、letを使用することを強く推奨します。
まとめ
今回の記事では、JavaScriptのスコープについて説明しました。
最後の項では「var宣言の問題点」に触れ、スコープの話題から逸れてしまいましたが、変数はletで宣言すべきである理由が改めてご理解いただけたかと思います。
本記事についての質問、誤りの指摘、ご意見ご感想などありましたら、ぜひコメント頂ければ幸いです。
最後までお読みいただき、ありがとうございました。


コメント