【JavaScript】スコープ - 変数の有効範囲

【JavaScript】スコープ - 変数の有効範囲

JavaScriptのスコープについて解説します。

検証環境

スコープ

スコープは“変数の有効範囲”です。

JavaScriptのスコープはグローバルスコープとローカルスコープに分類され、ローカルスコープは更にブロックスコープと関数スコープに分かれます。

グローバルスコープ

グローバルスコープは“プログラムのどこからでもアクセスが可能です。”

トップレベルで宣言した変数はこのスコープを持ちます。

___ih_hl_start
let scope = "global scope";
___ih_hl_end

console.log(scope);

function check() {
    console.log(scope);
}

check();
global scope
global scope

また、varキーワードで宣言したグローバルスコープの変数はWindowオブジェクトのプロパティに追加されます。

※ Windowオブジェクトはブラウザ実行のJavaScriptにおいて、最上位のオブジェクトです。

___ih_hl_start
var scope_v = "var global scope";
let scope_l = "let global scope";
const scope_c = "const global scope";
___ih_hl_end

console.log(window.scope_v);
console.log(window.scope_l);
console.log(window.scope_c);
var global scope
undefined
undefined

なお、上記サンプルから分かるようにletキーワード、constキーワードで宣言した変数はWindowオブジェクトに追加されません。

ローカルスコープ

ローカルスコープはブロックスコープと関数スコープに分かれます。

ブロックスコープ

ブロックスコープはブロック({})単位のスコープです。

ブロック内で宣言した変数がこのスコープを持ちます。

if( true ) {
    ___ih_hl_start
    let scope = 'block scope';
    ___ih_hl_end
    console.log(scope);
}

console.log(scope);
block scope
Uncaught ReferenceError: scope is not defined

このようにブロック外からアクセスするとエラーが発生します。

ただし、varキーワードの宣言変数はアクセスが可能になります。

if( true ) {
    ___ih_hl_start
    var scope = 'block scope';
    ___ih_hl_end
    console.log(scope);
}

console.log(scope);
block scope
block scope

関数スコープ

関数スコープは関数単位のスコープです。

関数で宣言した変数や仮引数がこのスコープを持ちます。

___ih_hl_start
function check( scope_a ) {
___ih_hl_end

    ___ih_hl_start
    let scope_v = 'function scope( variable )';
    ___ih_hl_end

    console.log(scope_a);
    console.log(scope_v);
}

check('function scope( argument )');

console.log(scope_a);
console.log(scope_v);
function scope( argument )
function scope( variable )
Uncaught ReferenceError: scope_a is not defined

このように関数外からアクセスするとエラーが発生します。

※ 11行目のconsole.log(scope_a)でエラーが発生しプログラムが停止していますが、console.log(scope_v)も同様のエラーになります。

任意ブロック

任意のブロックを作成し、スコープ化することができます。

{
    ___ih_hl_start
    let scope = 'block scope';
    ___ih_hl_end
    console.log(scope);
}

console.log(scope);
block scope
Uncaught ReferenceError: scope is not defined

ネスト

ブロックはネスト(階層化)できます。

ネストでは下層から上層の変数にアクセスできますが、その逆はできません。

{
    let scope_o = 'scope outer';
    {
        let scope_i = 'scope inner';
        ___ih_hl_start
        console.log(scope_o);
        ___ih_hl_end
    }
    ___ih_hl_start
    console.log(scope_i);
    ___ih_hl_end
}
scope outer
Uncaught ReferenceError: scope_i is not defined

5行目のアクセス(下層から上層)は成功していますが、7行目のアクセス(上層から下層)はエラーが発生しています。

また、スコープが異なれば同名の変数を宣言できます。

{
    let scope = 'scope outer';
    {
        ___ih_hl_start
        let scope = 'scope inner';
        ___ih_hl_end
        console.log(scope);
    }
    console.log(scope);
}
scope inner
scope outer

異なる変数として扱われるため、変数の操作(値の更新など)が影響することはありません。

ただし、var宣言変数は影響するので注意が必要です。

{
    var scope = 'scope outer';
    {
        ___ih_hl_start
        var scope = 'scope inner';
        ___ih_hl_end
        console.log(scope);
    }
    console.log(scope);
}
scope inner
scope inner