JavaScript 入門 | 関数 | 関数のスコープ (function scope)

関数のスコープ (function scope)

ほとんどのプログラミング言語では、ブロック内で定義された変数にブロック外からアクセスすることはできない。
ブロック (block) とは、コード内で中括弧 ({}) に囲まれた部分を指す。
このようなブロックを基準とするスコープを、ブロック単位のスコープという。

しかし JavaScript は他の言語とは異なり、ブロックの代わりに関数を使用する。
JavaScript で関数は、自分が定義されたスコープ内で定義されたすべての変数および関数にアクセスできる。

「グローバル関数」は、すべてのグローバル変数とグローバル関数にアクセスできる。
一方、別の関数内に定義された「内部関数」は、その関数の親関数 (parent function) で定義されたすべての変数、および親関数がアクセスできる他のすべての変数にもアクセスできる。

// x、y、name をグローバル変数として宣言する。
var x = 10, y = 20;
// sub() をグローバル関数として宣言する。
function sub() {
    return x - y;     // グローバル変数である x、y にアクセスする。
}
document.write(sub() + "<br>");
// parentFunc() をグローバル関数として宣言する。
function parentFunc() {
    var x = 1, y = 2; // グローバル変数と同じ名前で宣言し、グローバル変数の範囲を制限する。
    function add() {  // add() 関数は内部関数として宣言される。
        return x + y; // グローバル変数ではなくローカル変数 x、y にアクセスする。
    }
    return add();
}
document.write(parentFunc());

関数ホイスティング (hoisting)

JavaScript で関数のスコープとは、関数内で宣言されたすべての変数が関数全体にわたって有効であることを意味する。

ところが、このスコープの適用は、変数が宣言される前にも同じように適用される。
この JavaScript の特徴を関数ホイスティング (hoisting) という。
つまり、JavaScript 関数内にあるすべての変数宣言は、関数の最初へ移動されたかのように動作する。

var globalNum = 10;     // globalNum をグローバル変数として宣言する。
function printNum() {
    document.write("ローカル変数 globalNum 宣言前の globalNum の値は " + globalNum + " である。<br>"); // ①
    var globalNum = 20; // globalNum をローカル変数として宣言する。 // ②
    document.write("ローカル変数 globalNum 宣言後の globalNum の値は " + globalNum + " である。<br>");
}
printNum();

上の例の ① の時点では、変数 globalNum がグローバル変数を指していると考えがちである。
しかし JavaScript 内部では、関数ホイスティングによって次のようにコードが変更されて処理される。

ホイスティング後のコード

var globalNum = 10;
function printNum() {
    var globalNum; // 関数ホイスティングによって変数の宣言部分が関数の最初へ移動される。
    document.write("ローカル変数 globalNum 宣言前の globalNum の値は " + globalNum + " である。<br>");
    globalNum = 20;
    document.write("ローカル変数 globalNum 宣言後の globalNum の値は " + globalNum + " である。<br>");
}
printNum();

上の例の ① の時点では、globalNum というローカル変数が宣言だけされ、まだ初期化されていない状態である。
したがって、このとき globalNum 変数にアクセスすると、まだ初期化されていない変数へアクセスしたことになるため、undefined 値を返す。
実際に変数が初期化される時点は、元のコードで変数が宣言された ② の時点である。

JavaScript では関数ホイスティングが自動的に行われるが、常に関数ブロックの最初に変数を宣言するのがよい。