ブログ

日々の開発で得た知見や、技術的な発見をアウトプットしていきます。フロントエンド開発を中心に、実装のヒントやトラブルシューティング、新しい技術の検証結果など、実践的な情報を発信。エンジニアとしての成長の記録であり、同じ課題に直面する方々への導きとなることを目指しています。

BLOG

Javascriptのスコープを理解する

スコープは多くの言語に存在する「変数の有効範囲」のことです。

Javascriptには以下のようなスコープが存在します。

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

    ・関数スコープ

    ・ブロックスコープ

複雑なコードを書く場合、スコープを小さくすることでバグの原因を減らしたり、見通しをよくすることができます。

ローカルスコープ

ブロックスコープ

ブロックとは{}で囲まれたコードを指します。

if(true) {
  const block = 'block-true'; //ブロックスコープの変数
} else {
  const block = 'block-false'; //ブロックスコープの変数
}

上記のコードのようにif文の二つのブロックの中にそれぞれblockという変数を宣言することができます。

同じ変数名ですがそれぞれ別の変数として扱われます。

{
  const block = 'block-true'; //ブロックスコープの変数
  console.log(block);
} 

console.log(block); //ブロックの外なのでエラーになる

上記のコードだと2回目のconsole.logはブロックスコープの外で変数を利用しようとしているのでエラーになります。

関数スコープ

関数の中が有効範囲になるスコープです。

関数スコープ内で宣言された変数は、その関数内でのみ使うことができます。

function example() {
    var kansu = '';
    console.log(kansu); 
}

example();
console.log(kansu); // 関数の外なのでエラーになる

グローバルスコープ

最も広いスコープで、プログラムのどこからでも利用することができます。

関数に全く囲われてない最上位の領域で変数を宣言したり、関数内でも変数を宣言する際にvarやconst、letなどを付けずに使うとグローバルスコープになります。

// 最上位でグローバルスコープに変数を宣言
var globalVar = 'global';

// これもグローバルスコープに変数を宣言
globalVar1 = 'global1';

function example() {
  // varやconstがついてないのでグローバル変数
  globalVar2 = 'global2';
}

console.log(globalVar1); // 'global1'が出力

console.log(globalVar2); // まだ宣言していないのでここで呼ぶとエラー

example(); // 関数の中でグローバル変数globalVar2が宣言される
console.log(globalVar2); // 'global2'が出力

ブラウザにおけるグローバル変数はいwndowオブジェクトのプロパティとしても操作できます。

console.log(window.globalVar2);

変数の巻き上げ

関数スコープの性質として「同じ関数内で同名の変数を複数回宣言した場合に、同一の変数として扱われる」という動作があります。

以下のコードを見てください

function example() {
  var hostingVar1 = 'hostingVar1';
  console.log(hostingVar1);

  if(true) {
    var hostingVar1 = '変更';
    console.log(hostingVar1);
  }

  console.log(hostingVar1); // => ”変更”
}

varで書いた場合varで宣言された変数 hostingVar1 は関数スコープ内で有効です。

if文の中で同じ変数 hostingVar1 を再宣言した場合でも、変数の巻き上げ(hoisting)により、同じスコープ内で一つの変数として扱われます。

なので、ifブロック内での変更が外部の console.log(hostingVar1); にも影響します。

同じコードをletで書くと以下のようになります。

function example() {
  let hostingVar1 = 'hostingVar1';
  console.log(hostingVar1);

  if(true) {
    let hostingVar1 = '変更';
    console.log(hostingVar1);
  }
  //ブロック変数だから影響を受けない
  console.log(hostingVar1); // => ”hostiongVar1”
}

最初のhostingVar1と2回目のhostingVar1の変数はそれぞれ別のものとして扱われます

if文の中で同じ変数 hostingVar1 を再宣言しても、ブロックスコープ内で新しい変数が作成され、外部の変数には影響しません。

そのため、ifブロック内での変更が外部の console.log(hostingVar1); には影響しません。

constやletを使うと変数の巻き上げへの気配りをしなくて済むようになります。

まとめ

  • スコープとは有効範囲のこと
  • javascriptは変数の宣言の仕方によりブロックスコープ(const/let)、関数スコープ(var)などスコープの範囲が変化する
  • varでは変数の巻き上げがバグの原因になることがある