JavaScriptのスコープについて

2021/01/20に公開約2,600字

スコープとは

簡単に言うと変数がコードの中のどこから参照できるかの有効範囲。

JavaScriptのスコープは下記の二つに分類できる。

  • グローバルスコープ:コード全体から参照できる
  • ローカルスコープ:定義された関数の中でのみ参照できる

またグローバルスコープを持つ変数のことをグローバル変数ローカルスコープを持つ変数のことをローカル変数といいます。

関数スコープ

まず関数スコープについてみてみます。
下記のようにグローバル変数はコードのどこからでも参照できます。
ローカル変数は、変数を定義した関数の中でしか参照できないため、関数の外から参照しようとするとエラーになります。

const globalScope = 'グローバルスコープ';

function getValue() {
  const localScope = 'ローカルスコープ';

  console.log(globalScope); //グローバルスコープ
  console.log(localScope); //ローカルスコープ
}

getValue();
console.log(globalScope); //グローバルスコープ
console.log(localScope); //not defined

仮引数のスコープ

仮引数とは呼び出し元から関数に渡された値を受け取るための変数です。
下記の関数ではvalueが仮引数になります。

function decrementValue(value) {・・・}

基本型

まずは基本型について見てみます。
①でグローバル変数valueに10が代入されます。
②で関数decrementValueが呼び出されますが、その内部で使用されている仮引数valueはローカル変数と見なされるので、これをいくら操作してもグローバル変数valueに影響を与えることはありません。
なのでグローバル変数のvalueが書き換えられることはなく、③ではもともとの値である10が返されます。

let value = 10 // ①

function decrementValue(value) {
  value--;
  return value
}

console.log(decrementValue(100)); // ② 結果:99
console.log(value); // ③ 結果:10

参照型

次に仮引数に渡される値が参照型の場合を見てみます。
参照型とは値そのものではなく、値を格納したメモリ上の場所だけを格納している型です。

下記の例だと、①で定義されたグローバル変数valuesと、②で定義された仮引数は変数としては別物ですが、③でグローバル変数valuesの値が仮引数valuesに渡された時点で、結果的に実際に参照しているメモリ上の場所が等しくなります。

したがって、deleteItem関数の中で配列を操作した場合、その結果はグルーバル変数valuesにも反映されます。


let values = [1, 2, 3, 4, 5]; // ①

function deleteItem(values) { // ②
  values.pop();
  return values;
}

console.log(deleteItem(values)); // ③ 結果:[1, 2, 3, 4]
console.log(values); // ④ 結果:[1, 2, 3, 4]

ブロックスコープ

{}で囲んだ範囲をブロックといいスコープを作成します。
ブロックによるスコープのことをブロックスコープと呼びます。
ブロック内で宣言された変数は、スコープ内でのみ参照でき、スコープの外側からは参照できません。

下記の例を見てみると、ブロック内で変数valuesが宣言されています。
①でブロック外から、ブロック内の変数を参照していますが、ReferenceErrorとなっていまします。

{
  const value = 10;
  console.log(value);
}

console.log(value); // ① 結果:ReferenceError

if文やwhile文などもブロックスコープを作成します。
単独のブロックと同じく、ブロックの中で宣言した変数は外から参照できません。


if (true) {
    const value = 10;
    console.log(value); // 結果:10
}
console.log(value); // 結果:ReferenceError

for文は、ループごとに新しいブロックスコープを作成します。
下記のコードでは、ループごとにconstでelement変数を定義していますが、エラーなく定義できています。
これは、ループごとに別々のブロックスコープが作成され、変数の宣言もそれぞれ別々のスコープで行われるからです。


const values = [1, 2, 3, 4, 5];

for (const element of values) {
    // forのブロックスコープの中でのみ`element`を参照できる
    console.log(element);
}

console.log(element); // 結果:ReferenceError

switch分は、条件分岐全体として1つのブロックです。
caseはブロックではないため、case句の単位に変数let宣言した場合はエラーとなります。

switch(x) {
  case 0:
    let value = 'x:0';
  case 1:
    let value = 'x:1'; // 変数名の重複
}

Discussion

ログインするとコメントできます