JavaScript - 変数のスコープ

変数のスコープ

プログラムのソースコード中における変数の有効範囲を スコープ と言います。変数にはグローバル変数ローカル変数があり、それぞれスコープの範囲が異なります。グローバル変数のスコープは、プログラム全体になります。ローカル変数のスコープは、その変数が宣言された関数の中だけに限定されます。関数の引数もローカル変数になるため、引数のスコープは関数の中だけに限定されます。

関数の中にグローバル変数と同じ名前のローカル変数があった場合は、ローカル変数が優先されます。グローバル変数と同じ名前でローカル変数や引数を宣言するとグローバル変数が無効になります。

var scope = "global";   // グローバル変数を宣言する。

function checkscope() {
  let scope = "local";  // 同じ名前でローカル変数を宣言する。
  return scope;         // ローカル変数の値が返される。グローバル変数の値ではない。
}

checkscope();           // "local"

宣言文を省略すると、次のようになります。

scope = "global";          // var 文を省略してグローバル変数を宣言する。

function checkscope2() {
  scope = "local";          // let を省略するとグローバル変数が変更される。
  myscope = "local";        // 新たなグローバル変数が暗黙的に宣言される。
  return [scope, myscope];  // 2 つの値を返す。
}

console.log(checkscope2()); // => ["local", "local"]
console.log(scope);         // => "local": グローバル変数が変更された。
console.log(myscope);       // => "local": グローバル変数が追加された。

ネストした関数では、それぞれの関数ごとに独立したローカルスコープを持ちます。各ローカルスコープはネスト構造になります。

var scope = "global scope";     // グローバル変数。

function checkscope() {
  let scope = "local scope";    // ローカル変数。
  function nested() {
    let scope = "nested scope"; // ネストされたローカル変数。
    return scope;               // このスコープでの値が返される。
  }
  return nested();
}

console.log(checkscope());      // "nested scope"

関数のスコープとホイスティング

プログラミング言語の中には、中括弧で囲まれたブロックごとにスコープを持つものがあります。このようなプログラミング言語では、変数は宣言されたブロックの外からは参照できません。このようなスコープを ブロックスコープ と呼びますが JavaScript にはこのようなブロックスコープはありません。JavaScript では、その代わりに 関数スコープ を使います。

変数は、その変数が定義された関数の中からアクセスできます。以下のコードでは変数 i、j、k が異なる場所で宣言されていますが、スコープはすべて同じです。そのため 3 つの変数は関数の本体のどこからでもアクセスできます。

function test(o) {
  var i = 0;
  if (typeof o == "object") {
    var j = 0;
    for(var k=0; k < 10; k++) {
      console.log(k);           // 0 から 9 まで出力する。
    }
    console.log(k);             // 10
  }
  console.log(j);               // 0
}

JavaScript では変数宣言よりも前のコードからでも変数にアクセスできます。この特徴的な仕組みはホイスティング (巻き上げ) と呼ばれています。JavaScript では変数宣言を、関数の先頭にホイスティングしたかのように振る舞います。

function f() {
  console.log(scope);  // undefined
  var scope = "local";
  console.log(scope);  // local
}

実行時にローカル変数にアクセスは可能ですが var 文がまだ実行されていないため undefined と出力されます。上記のコードは、以下のコードと同じ意味になります。

function f() {
  var scope;          // 関数の先頭でローカル変数が宣言される。
  console.log(scope); // 値は "undefined" が出力される。
  scope = "local";    // 変数を初期化する。
  console.log(scope); // 初期化した値が出力される。
}

プロパティとしての変数

var を使って変数を宣言すると再定義不可 (nonconfigurable) のプロパティになります。再定義不可のプロパティは delete 演算子でも削除できません。var で宣言していない変数は、自動的にグローバル変数になりますが再定義可能 (configurable) なプロパティになるため削除できます。

var var1 = 1;
var2 = 2;

delete var1  // false: 変数は削除されない
delete var2  // true : 変数は削除される

スコープチェーン

スコープチェーンとは、スコープ内にある変数を定義するオブジェクトのリスト (チェーン) です。JavaScript が変数 x の値を調べる場合 (名前解決する場合)、チェーンの先頭のオブジェクトから検索を始めます。x プロパティが存在する場合、そのプロパティの値が使われます。x プロパティが存在しない場合、ReferenceError が発生します。

function outside() {
  var x = 10;
  function inside(x) {
    return x;
  }
  return inside;
}
result = outside()(20); // 20

同じ名前の 2 つの引数や変数がある場合、名前衝突が生じます。より内部のスコープが優先されるので、内側のスコープほど優先度が高く、外側のスコープほど優先度が低くなります。

return x の場所で inside の引数 xoutside の変数 x との間に名前衝突が起こっています。ここでのスコープチェーンは inside, outside, グローバルオブジェクト です。したがって insidexoutsidex より優先され 20が返されます。

関連記事

JavaScript のまとめページはプログラミング JavaScript 入門を参照してください。

Category:
プログラミング
公開日:
更新日:
Pageviews:
28
Shares:
0
Tag:
JavaScript
hatebu icon
hatebu