JavaScriptのスコープについて

jsコード JavaScript

プログラミングにおける「スコープ」とは、有効範囲のことで、スコープの対象となるのは主に変数、定数、関数です。

スコープはまずグローバルスコープとローカルスコープに大別され、ローカルスコープはさらに関数スコープとブロックスコープに分けられます。

  • グローバルスコープ:ファイル内のどこからでも参照できる
  • ローカルスコープ
    • 関数スコープ:関数の中でのみ参照できる
    • ブロックスコープ:波カッコで囲まれたブロックの中でのみ参照できる

変数と定数のスコープ

JavaScriptの変数宣言に使用するキーワードは、2種類存在します。
let と var ですね。

let だけであれば話は単純なのですが、var が絡んでくると少々ややこしくなります。

一言で違いを表現すると、varで宣言された変数には、ブロックスコープがありません

グローバルスコープ

関数やブロックに囲まれていない場所で宣言した変数(let宣言)および定数は、グローバルスコープを持ち、ファイルのどこからでも参照できます。

仮にそれがHTMLファイル内に書かれた別々の scriptタグであってもです。

scope.html
<!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宣言)および定数は、関数スコープを持ち、関数内からのみ参照できます。

scope.html
<!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ブロックなどがあります。

scope.html
<!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 宣言された変数の場合

scope.html
<!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で宣言すべきである理由が改めてご理解いただけたかと思います。


本記事についての質問、誤りの指摘、ご意見ご感想などありましたら、ぜひコメント頂ければ幸いです。

最後までお読みいただき、ありがとうございました。

『詳説 JavaScript』メニューに戻る

コメント

タイトルとURLをコピーしました