JavaScriptのスコープ(グローバル、ローカル)

2025-07-28

スコープの基本概念

スコープとは、変数や関数がアクセス可能な範囲を決定する仕組みです。JavaScriptには主に以下のスコープがあります:

  • グローバルスコープ:コードのどこからでもアクセス可能
  • ローカルスコープ:特定の範囲内でのみアクセス可能
  • 関数スコープ
  • ブロックスコープ(ES6以降)

グローバルスコープ

グローバル変数の定義

// グローバル変数
const globalVar = 'グローバル変数';

function checkGlobal() {
  console.log(globalVar); // アクセス可能
}

checkGlobal();
console.log(globalVar); // アクセス可能

注意点

  • グローバル変数はどこからでも変更可能
  • 名前衝突のリスクがある
  • 可能な限り避けるべき

意図しないグローバル変数

function createGlobal() {
  // var, let, constを付け忘れるとグローバル変数になる
  accidentalGlobal = '意図しないグローバル変数';
}

createGlobal();
console.log(accidentalGlobal); // アクセス可能(非推奨)

ローカルスコープ

関数スコープ

function showLocal() {
  // ローカル変数
  const localVar = 'ローカル変数';
  console.log(localVar); // アクセス可能
}

showLocal();
// console.log(localVar); // エラー(アクセス不可)

ブロックスコープ(ES6)

if (true) {
  // let/constはブロックスコープ
  let blockVar = 'ブロック変数';
  const BLOCK_CONST = 'ブロック定数';
  console.log(blockVar); // アクセス可能
}

// console.log(blockVar); // エラー(アクセス不可)
// console.log(BLOCK_CONST); // エラー(アクセス不可)

varの注意点

if (true) {
  var functionScoped = '関数スコープ';
}
console.log(functionScoped); // アクセス可能(ブロックスコープではない)

スコープチェーン

JavaScriptは変数を探す際、現在のスコープから外側のスコープへと順に探します。

const global = 'グローバル';

function outer() {
  const outerVar = '外側';

  function inner() {
    const innerVar = '内側';
    console.log(innerVar); // '内側'
    console.log(outerVar); // '外側'
    console.log(global);   // 'グローバル'
  }

  inner();
  // console.log(innerVar); // エラー(アクセス不可)
}

outer();

レキシカルスコープ(静的スコープ)

JavaScriptのスコープはコードを書いた時点で決定されます(実行時ではない)。

const name = 'グローバル';

function logName() {
  console.log(name);
}

function wrapper() {
  const name = 'ローカル';
  logName(); // 'グローバル'(定義時のスコープが使われる)
}

wrapper();

クロージャ

関数が定義された時のスコープを「覚えて」いる機能。

function createCounter() {
  let count = 0;

  return function() {
    count++;
    return count;
  };
}

const counter = createCounter();
console.log(counter()); // 1
console.log(counter()); // 2

即時実行関数式

即時実行関数式(IIFE:Immediately Invoked Function Expression)は、定義してすぐに実行される関数のことです。

// 基本構文
(function() {
  // ここにコードを書く
})();

// ES6のアロー関数で
(() => {
  // ここにコードを書く
})();

即時実行関数式をなぜ使うのか?(メリット)

  • 一度しか実行しないコードを書くときに便利
  • 変数のスコープを限定して、グローバル空間を汚さないため

JavaScriptで「スコープの分離」や「グローバル汚染の防止」によく使われます。

スコープのベストプラクティス

  1. グローバル汚染を避ける
  • IIFE(即時実行関数式)を使用
  • モジュールパターンを採用
  1. constを優先
   // 👍 推奨
   const localVar = '値';

   // 👎 非推奨
   var localVar = '値';
  1. ブロックスコープを活用
   // ループ内でlet/constを使用
   for (let i = 0; i < 5; i++) {
     // iはループ内のみ有効
   }
  1. 名前空間の使用
   // グローバルを汚さない
   const MyApp = {};
   MyApp.utils = {};

スコープ関連の特殊なケース

ホイスティング(変数の巻き上げ)

console.log(hoistedVar); // undefined(エラーにならない)
var hoistedVar = '値';

// let/constはホイスティングされるがTDZ(Temporal Dead Zone)にある
// console.log(notHoisted); // エラー
let notHoisted = '値';

関数のホイスティング

hoistedFunc(); // '動作する'

function hoistedFunc() {
  console.log('動作する');
}

// 関数式はホイスティングされない
// notHoistedFunc(); // エラー
const notHoistedFunc = function() {};

モジュールパターン

const MyModule = (function() {
  // プライベート変数
  let privateVar = '外部からアクセス不可';

  // プライベート関数
  function privateMethod() {
    return privateVar;
  }

  // 公開するインターフェース
  return {
    publicMethod: function() {
      return privateMethod();
    }
  };
})();

console.log(MyModule.publicMethod()); // '外部からアクセス不可'
// console.log(MyModule.privateVar); // undefined

演習問題

初級問題(3問)

  1. 次のコードの出力結果は?
   let x = 10;

   function test() {
     let x = 20;
     console.log(x);
   }

   test();
   console.log(x);
  1. 次のコードの間違いを指摘し、修正しなさい。
   function printNumbers() {
     for (var i = 0; i < 3; i++) {
       console.log(i);
     }
     console.log('最後:', i);
   }
   printNumbers();
  1. 次のグローバル変数をIIFEを使って保護しなさい。
   let counter = 0;

   function increment() {
     counter++;
     console.log(counter);
   }

中級問題(6問)

  1. 次のコードの出力結果とその理由を説明しなさい。
   var a = 1;
   let b = 2;

   {
     var a = 3;
     let b = 4;
     console.log(a, b);
   }

   console.log(a, b);
  1. 次のクロージャ関数を作成しなさい。

createMultiplier(n)は、引数をn倍する関数を返す。

   const double = createMultiplier(2);
   console.log(double(5)); // 10
  1. 次のコードのホイスティング後の状態を書きなさい。
   console.log(value);
   var value = 10;
   function test() {
     console.log('テスト');
   }
  1. 次のコードの出力結果を順に答えなさい。
   let x = 1;

   function outer() {
     let x = 2;

     function inner() {
       console.log(x);
       let x = 3;
     }

     inner();
   }

   outer();
  1. 次のコードをブロックスコープを使用してリファクタリングしなさい。
   function processData(data) {
     if (data) {
       var processed = data.toUpperCase();
       console.log(processed);
     }
     console.log(processed); // アクセス可能だが非推奨
   }
  1. 次のモジュールパターンを完成させなさい。
   const Calculator = (function() {
     // ここにプライベート変数/関数を定義

     return {
       // ここに公開メソッドを定義
       add: function(a, b) { /* ... */ }
     };
   })();

上級問題(3問)

  1. 次のコードの出力結果とその理由を詳細に説明しなさい。
for (var i = 0; i < 3; i++) { 
    setTimeout(function() { 
        console.log(i);
    }, 100); 
}
for (let j = 0; j < 3; j++) { 
    setTimeout(function() { 
        console.log(j);
    }, 100);
}
  1. 次のような動作をするcreatePrivateStack()関数を実装しなさい。
const stack = createPrivateStack(); 
stack.push(10); 
stack.push(20); 
console.log(stack.pop()); // 20 
console.log(stack.items); // undefined(直接アクセス不可)
  1. スコープチェーンを利用して、次のような動作をするcreateScopeChain()関数を実装しなさい。
const scope = createScopeChain(); 
scope.set('x', 10); 
scope.set('y', 20); 
const childScope = scope.createChild(); 
childScope.set('x', 30); 
console.log(childScope.get('x')); // 30 
console.log(childScope.get('y')); // 20(親スコープから取得) 
console.log(childScope.get('z')); // undefined

解答例

初級問題解答

  1. 出力結果
   20
   10
    let x = 10;

    function test() {
        let x = 20;
        console.log(x); // 20が出力される
    }

    test();
    console.log(x); // 10が出力される
  1. 間違いと修正
   // varをletに変更
   function printNumbers() {
     for (let i = 0; i < 3; i++) {
       console.log(i);
     }
     // console.log('最後:', i); // エラー(iはブロックスコープ内のみ)
   }
  1. IIFEを使用した修正
   const counterModule = (function() {
     let counter = 0;

     return {
       increment: function() {
         counter++;
         console.log(counter);
       }
     };
   })();

   counterModule.increment();

中級問題解答

  1. 出力結果と理由
3 4
3 2
    var a = 1;
    let b = 2;

    {
        var a = 3;
        let b = 4;
        console.log(a, b);  // 3 4 ブロック内の値(letはブロックスコープ)
    }

    console.log(a, b); // 3 2 グローバルスコープの値(varはブロックスコープを無視して上書き)
  1. createMultiplier関数
   function createMultiplier(n) {
     return function(x) {
       return x * n;
     };
   }
  1. ホイスティング後の状態

ホイスティング順番

   var value;
   function test() {
     console.log('テスト');
   }

   console.log(value); // undefined
   value = 10;

コードに順番の付け加え


    // ①var value; 暗黙的value変数宣言
    console.log(value);  // ③ console.log()の実行
    var value = 10;      // ④ console.log()の実行
    function test() {    // ② function test()宣言とロード
        console.log('テスト');
    }
  1. 出力結果

ReferenceError: Cannot access 'x' before initialization
// letのTDZ(一時的デッドゾーン)に引っかかる

    let x = 1;

    function outer() {
        let x = 2;

        function inner() {
            console.log(x); // ブロックスコープの変数を参照するためエラーとなる
            let x = 3;
        }

        inner();
    }

    outer();
  1. ブロックスコープを使用したリファクタリング
   function processData(data) {
     if (data) {
       let processed = data.toUpperCase();
       console.log(processed);
     }
     // console.log(processed); // エラー(アクセス不可)
   }
  1. モジュールパターンの完成例
   const Calculator = (function() {
     let memory = 0;

     function add(a, b) {
       return a + b;
     }

     return {
       add: add,
       addToMemory: function(x) {
         memory += x;
         return memory;
       }
     };
   })();

上級問題解答

  1. 出力結果と理由

3 3 3 // varはループ終了後の値(関数スコープ)
0 1 2 // letはループごとに新しい変数(ブロックスコープ)

for (var i = 0; i < 3; i++) { 
    setTimeout(function() { 
        console.log(i); // 3回「3」が出力される
    }, 100); 
}
for (let j = 0; j < 3; j++) { 
    setTimeout(function() { 
        console.log(j); // 0, 1, 2 が順に出力される
    }, 100);
}
  1. createPrivateStack関数
function createPrivateStack() { 
    const items = []; 
    return { 
        push: function(item) { 
            items.push(item); 
        }, 
        pop: function() { 
            return items.pop();
        }
    };
}
  1. createScopeChain関数
function createScopeChain(parent) { 
    const vars = new Map(); 
    return { 
        set: function(key, value) { 
            vars.set(key, value); 
        }, 
        get: function(key) { 
            if (vars.has(key)) return vars.get(key); 
            if (parent) return parent.get(key); 
            return undefined; 
        }, 
        createChild: function() { 
            return createScopeChain(this); 
        }
    }; 
}