web codery

JavaScriptの変数, 関数のスコープと有効になるタイミング

category: JavaScript

今回は、JavaScriptの変数・関数のスコープ(有効範囲)と有効になるタイミングについてです。初めに、変数・関数宣言の書き方と特性について簡単にふれ、その後でスコープと有効になるタイミングについて説明していきます。
※ECMAScript4~5相当の説明です。

項目を飛ばして読んでいただいても大丈夫です。特定の項目を読みたい場合は、目次のリンクから飛ぶことができます。

目次

  1. 変数, 関数宣言の書き方と特性
  2. 変数のスコープ
  3. 変数, 関数が有効になるタイミング

変数, 関数宣言の書き方と特性

まずは、書き方と特性について簡単に確認しておきます。まずはコードを書いて、その後に補足を書きます。

/* 変数宣言 */
var emptyObject = {};

/* 関数宣言 */
function func () {
  /* ... */
}

/* 無名関数を代入しているが、こちらは変数宣言 */
var hoge = function () {
  /* ... */
};

/* 関数宣言で定義したものも変数として使える */
emptyObject =  func;

変数はvar のキーワードで宣言します。関数宣言はfunctionからの構文で宣言します。JavaScriptでは無名関数を使用することができ、変数を宣言すると同時に関数を代入する構文を使うことができます。

上のコードの最後に書いた箇所では、emptyObject変数にfunc関数を代入して中身を上書きしています。JavaScriptの関数は関数宣言したものでも変数として使うことができますが、実行できるようになるタイミングが変数宣言したものと違います(詳しくは後述)。

変数のスコープ

ローカルスコープが形成されるのは関数の内部だけです。関数内部で宣言された変数は、その関数内のローカル変数として扱われ、外側からはアクセスできません。関数内からその外側で宣言された変数にアクセスすることはできます。※以下のコードでは、スラッシュ2つのコメントで変数の出力を表します。

var globalVar = "グローバル";

function hoge() {
  var localVar = "ローカル";

  innerGlobal = "var忘れでグローバル変数!";

  console.log(globalVar);  // グローバル
  console.log(localVar);   // ローカル
}
console.log(innerGlobal);  // var忘れでグローバル変数!
console.log(localVar);     /* Error */

上記のinnerGlobalのようなvarなし代入は、エラーにならずにグローバル変数として扱われてしまうので注意が必要です(strictモードではエラーになります)。

どの関数にも属さない変数はグローバル変数として扱わます。webページで複数のスクリプトファイルを読み込んでいる場合でもグローバル空間は1つなので、読み込んだファイル間でグローバル変数を共有します。

複数のスコープを入れ子にする

関数を入れ子にすることで階層型のスコープを形成することができ、同じ名前の変数が複数のスコープで宣言された場合、まずそのスコープ内で宣言されたものを探し無ければひとつ外側のスコープを探すことを繰り返し、より内側で宣言された変数を参照します。

var scope = "グローバル";

function func() {
  var scope = "ローカル";
  console.log(scope);    // ローカル

  function innerFunc1() {
    var scope = "インナーローカル";
    console.log(scope);  // インナーローカル
  }

  function innerFunc2() {
    scope = "上書き";
  }
  innerFunc2();
  console.log(scope);    // 上書き
};
func();
console.log(scope);      // グローバル

上ではscopeという名前の変数を複数宣言していますが、グローバルに宣言したもの、func内の先頭で宣言したもの、innerFunc1内で宣言したものは、それぞれのスコープで宣言した別の変数として扱われます。

innerFunc2内で参照しているscope変数はfunc内の先頭で宣言したものなので、innerFunc2を実行すると”上書き”の文字列で内容が上書きされて、その後のアクセスでは”上書き”が出力されます。innerFunc1は上のコードでは実行されていませんが、実行された時点で関数内に処理が移り”インナーローカル”が出力されます。

変数, 関数が有効になるタイミング

変数に代入した値が有効になるのは代入した時点、関数宣言で書かれた関数の内容が有効になるのはスコープの先頭(宣言よりも前)です。

func();           /* 問題なく実行される */
anonymousFunc();  /* Error */

function func() {
  /* ... */
}

var anonymousFunc = function () {
  /* ... */
};

ここで注意したいのは、変数自体は宣言されたスコープの先頭で有効になっている点です。

var timing = "グローバル";

var func = function () {
  console.log(timing);  // undefined

  var timing = "ローカル";
  console.log(timing);  // ローカル
};

func内のtimingの変数宣言は最初の出力より後ですが、変数自体はfunc内の先頭で有効になっているため、グローバルスコープで宣言されたtimingではなくローカルに宣言したtimingが最初の出力時点でも参照されています。ただ、値がまだ代入されていないためundefinedになってしまいます。

以上、JavaScriptの変数・関数のスコープと有効になるタイミングでした。ECMAScript 5で導入されたstrictモードでは、var宣言なしの変数への代入文はエラーになりますし、ECMAScript 6ではletでブロックスコープの変数を宣言できるようになるようです。その辺りについては、今後一般的に使われるようになる頃に改めて書こうと思います。

管理人にメッセージを送る





ページトップ