JavaScriptを習得したい

on Note

変数には有効範囲がある

変数を定義して、その変数をいざ使おうとした時、どの場所からでもその変数を参照できるというものでもありません。変数を参照しようとした場所、またはその変数を定義した場所によって、その変数を参照することができるかどうかが決まります。

言い換えれば、変数には有効範囲があるということです。

宣言した変数が有効となる範囲のことをスコープと呼びます。スコープは、変数がどの場所から参照できるのかを定義する概念です。

ある変数を参照しようとした時、その変数のスコープ内であれば参照できるが、スコープの外からは参照できずエラーとなります。

グローバルスコープとローカルスコープ

スコープの考え方はグローバルとローカルに大別されます。その変数がどこからでも参照することができる(グローバルスコープ)変数なのか、そうではなく局所的な(ローカルスコープ)変数なのかです。

スクリプトにおいて最上位の階層がグローバルスコープにあたります。階層は{ }(波括弧)によって作られます。関数の処理を記述する部分などで用いる{ }です。

{ }で囲んだ部分をブロックと呼び、ブロックの中がローカルスコープにあたります。つまりブロックの中で宣言する変数がローカルスコープの変数ということです。

グローバルスコープの変数を、全ての場所から参照することができる変数ということでグローバル変数と呼びます。ローカルスコープの変数をローカル変数と呼びます。

let x = 5;

{
    let y = 10;

    console.log(x);     // 5を出力
    console.log(y);     // 10を出力
}

console.log(x);     // 5を出力
console.log(y);     // エラーになる

上記のコードでは、変数xはどこからでも参照することができます。

変数yは、ブロックの中で宣言したローカル変数なので、そのブロックの中でしか参照することができません。変数yをブロックの外で参照するとエラーになります。

同じスコープでの重複宣言は再宣言になる

同じスコープで、同じ変数名で宣言した場合、再宣言扱いになるのでエラーとなります。

let x = 5;
let x = 7;    // 変数xの再宣言(エラー)
{
    let y = 10;
    let y = 15;    // 変数yの再宣言(エラー)
}

上記の変数xと変数y、ともにエラーになります。letキーワードを使って、同じ階層で「x」(または「y」)を宣言し直している為です。

もし値を入れ直すのなら下記のように記述しないといけません。

let x = 5;
x = 7;    // 7を再代入
{
    let y = 10;
    y = 15;    // 15を再代入
}

上記はそれぞれ、再代入なのでエラーとなりません。(これがconstキーワードを使った宣言だったとしたら、constキーワードは再代入もエラーとなります。)

階層が変われば再宣言となりません。

let x = 5;
{
    let x = 20;
    console.log(x);    // 20を出力
}
console.log(x);    // 5を出力

上記は、階層が違うので再宣言にはなりません。ただし、それぞれ出力されている値に注目です。

最上位の階層でのconsole.log(x);では、グローバル変数のxを参照しています。下の階層の変数は参照できない為です。

ブロックの中のconsole.log(x);では、上位層でも変数xを宣言していますが、同じブロックでも変数xを宣言しています。こういった場合、直近の変数を参照します。よってここでは20を出力します。

複数の階層での有効範囲

変数の有効範囲は、宣言した階層と、その下(つまり宣言した場所以下)の階層になるということです。宣言した階層より上の階層は、ブロックの外になる為、その変数を参照することはできません。

グローバル変数は、そのブロックも自分以下の階層となる為、どこからでも参照することができるのです。

ブロックは入れ子にして、複数の階層にすることができます。

{
    let y = 10;
    {
        let z = 20;
        console.log(y);     // 10を出力
        console.log(z);     // 20を出力
    }
    console.log(y);     // 10を出力
    console.log(z);     // エラーとなる
}

ブロックスコープと関数スコープ

{ }(波括弧)で囲まれたスコープは局所的ということでローカルスコープでした。{ } は単独で用いることもあれば、関数やif文などのブロックとしても用います。

関数のブロックでのスコープを関数スコープ、それ以外のブロックでのスコープをブロックスコープと、区別されることがあります。

  • グローバルスコープ
  • ローカルスコープ
    • ブロックスコープ
    • 関数スコープ

最上位の階層にあたるグローバルスコープとブロックで囲まれた局所的なローカルスコープに大別され、さらにローカルスコープは、ブロックスコープと関数ブロックに区別されることがあります。

有効範囲:ブロックスコープ

{ }(ブロック)の中で定義した変数は、そのブロックの外からは参照することができません。ローカル変数だからです。

有効範囲:関数スコープ

関数に対しての場合を関数スコープと呼びます。関数スコープで定義した変数は、その関数のブロックの外からは参照することができません。ローカル変数だからです。

関数の仮引数も同じく関数スコープを持ちます。つまり仮引数は、他の関数からは利用できません

変数の有効範囲のまとめ

変数の有効範囲は次の2点と言えます。

  • 宣言した階層のブロックスコープや関数スコープで有効。
  • 同じ階層だけでなく、宣言より下の階層でも有効。

宣言した階層より上の階層で使おうとするとエラーが出ます。

letで宣言した変数の有効範囲

ブロックスコープという概念が便利な理由

変数の再宣言(再定義)はエラーになります。つまり変数の宣言をする際、すでに使っている変数名で宣言するとエラーになる、ということです。

もしブロックスコープという概念がなかった場合、宣言の際に付けようとしている変数名が先に使われていないか、コード全てから確認しないといけなくなります。(グローバル変数は全ての範囲に影響する為です。)

ブロックスコープがあれば、再宣言扱いとなるのは同じブロックの中だけなので、宣言しようとしているブロックだけを確認すれば良いわけです。

ブロックスコープの概念は、変数名を決める時だけでなく、エラーが起きた際もその恩恵を受けることになります。

グローバル変数であれば、その変数が影響を与えるのはプログラム全体になりますが、ローカル変数が影響するのは、そのブロック以下です。ブロックスコープの下で変数を宣言し、その変数が影響する範囲を必要最低限に抑えることで、エラーが起きにくくし、またエラーが起きた時の範囲も限定することができます。