JavaScript


このJavaScriptの解説は以下の本の2章と3章を元にしています。

Java開発者のためのAjax実践開発入門
河村嘉之、川尻剛、福沢知海(著)
技術評論社
ISBN978-4-7741-3297-6

JavaScript

特徴

JavaScriptはインタプリタ型のスクリプト言語で、1995年に Netscape Communications によって開発され netscape ブラウザに搭載されました。 続いてMicrosoftもよく似たJScriptを開発してInternet Explorerに搭載したのですが、 両者の互換性が低かったことが問題となり、1997年に 国際標準化機関 Ecma International がECMAScript (ECMA 262) として標準化しました。 その後、1999年に発表されたECMAScriptの第3版である ECMA 262-3 に対して、 両者が準拠することを表明したことにより、標準化が完了しました (ただし、過去への互換性のために挙動が異なる部分が残っています)。 その後、2009年に5版が、2011年に5.1版が公開されていますが、現時点(2014年5月)では、 有名どころのブラウザではECMA 262 3版をサポートしているものが多いようです。

JavaScriptは 名前が似ていますがJavaとは全く別の言語です。

注意点

変数

JavaScriptの変数には型の区別がないが、格納する値には型がある。

EMCAscriptでは次の6種類の型が定義されている。

JavaScriptにおいて、値の型を判定する演算子であるtypeofは、 Null型の値に対してオブジェクト型と返し、 ある種のオブジェクト型の値に対して "function" と返したりする。 したがって、プログラム的には次のような分類で考えた方がよい。 (typeofが返す値を括弧内に示している。)

変数の型によって値の格納のされ方が異なる。

複合型は0個以上のプロパティから構成される型である。 プロパティとは名前と値のペアであり、オブジェクト名にドット(.)をつけることで アクセスができる。

  var obj = {};  // 変数objをからオブジェクトで初期化
  obj.a = 1;     // obj内のaプロパティに1を代入
  alert(obj.a);  // obj.aの値を参照して表示する。

プリミティブ型にはそれぞれに対応するオブジェクト型が wrapper クラスとして存在している。 数値型(number)に関しては、Numberオブジェクトが対応する。 プリミティブ型の値に対してプロパティアクセス演算子を使用すると、対応するwrapper クラスのインスタンスが自動的に生成される。

  var a = 3.14;
  a.a = 1;     // (new Number(a)).a = 1; と同じこと
  alert(a.a);  // (alert((new Number(a)).a)) と同じこと --> "undefined"
  alert((3.14).toExponential()); // alert((new Number(3.14)).toExponential())と同じこと --> "3.14e+0"

オブジェクト型には2通りの生成方法がある。

  var x = new Object();   // new 演算子で生成する
  x.a = 1;
  x.b = 2;
  var y = {  // Objectリテラルを用いて生成する
    a:1,
    b:2
  };

配列型の値に対して typeof は "object" を返す。

  var x = new Array();
  x[0] = 1;
  x[1] = 2;
  var y = [1,2];

一般のobjectでも、プロパティ名を用いて配列のようにアクセスすることができる。

  var obj = { a:1, b:2, c:3 };
  console.log( obj['b'] );

関数型はデータであると同時に、実行することができる。 関数型はデータであるから、変数やプロパティに自由に格納できる。

関数を生成する方法は3通りある。

  var func = new Function('a, b', 'return a + b;'); //「関数オブジェクト」として生成する
  // 第1引数が仮引数のリスト、第2引数が関数本体

  function func(a,b) {               // 「関数定義」とよぶ宣言形式
    return a + b;
  };

  var func = function (a,b) {        // 「関数リテラル」で生成する
    return a + b;
  };

  alert(func(1,2));   // 実引数を与えて実行する。この場合は3と表示される。

スコープ

スコープ(scope)とは、変数名や関数名といった識別子を特定できる領域を指します。 Javaとは異なり、JavaScriptでは「関数の外側のグローバルスコープ」と 「関数の内側の関数スコープ」の2種類しかない。 つまり、for文やif文のようなブロックレベルのスコープが存在しない。

  if (true) {
    var a = 3;
  }
  console.log(a);   // 「3」

各スコープは、内側から外側に向かって変数を参照できる。 関数スコープからはグローバルスコープで宣言された変数を参照できるが、逆はできない。 また、関数は入れ子で定義できるが、この場合も内側の関数が外側で宣言された 変数を参照できる。

  var g = 1;
  function outer() {
    var a = 2;
    function inner() {
      var b = 3;
      // 参照できるもの: g, a, b
    }
    // 参照できるもの: g, a
  }
  // 参照できるもの: g

JavaScriptの変数は、インタプリタ内部では全て特殊なオブジェクトのプロパティとして 具体化されている。この特殊なオブジェクトを「変数オブジェクト」と呼ぶ。 変数オブジェクトには2種類あり、グローバルスコープに相当するGlobalオブジェクトと、 関数スコープに相当するActiviationオブジェクトがある。 変数オブジェクトのつながりをスコープチェーンと呼び、インタープリタは このスコープチェーンをたどることで指定された変数を探す。

Globalオブジェクトはスクリプトのどこでも参照できるもので、インタープリタの 起動時に1つだけ生成される。

  1. 読み込んだスクリプト中の、全ての変数宣言と関数定義を確認する。
  2. 関数定義と同名のプロパティを作成し、指定された内容で関数型の値を 生成して格納する。同名のプロパティが存在する場合は上書きする。
  3. 変数宣言と同名のプロパティを作成し、undefinedで初期化する。 同名のプロパティが存在する場合は何もしない。
  4. スクリプトを実行する。
  var a = 1;
  function b() {};
  console.log(window.a);  // 「1」
手書きの説明
Activiationオブジェクトは、関数を実行するたびに生成されるオブジェクトで、
関数内部の変数をプロパティとして具体化する。
  1. 引数と同名のプロパティを作成し、指定された値で初期化する。 指定された個数が足りない場合は残りをundefinedで初期化する。
  2. argumentプロパティを作成し、引数で指定された値で初期化する。
  3. 実行する関数中の、全ての変数宣言と関数定義を確認する。
  4. 関数定義と同名のプロパティを作成し、指定された内容で関数型の 値を生成して格納する。同名のプロパティが存在する場合は上書きする。
  5. 変数宣言と同名のプロパティを作成し、undefinedで初期化する。 同名のプロパティが存在する場合は何もしない。
  6. 関数を実行する。
  function test(a,b) {
    var c = a;
    function d() {}
  }
  test(1,2);
手書きの説明

関数は内部に[[Scope]]という特殊なプロパティを持ち、ここに変数オブジェクトのリストが格納される。 これがスコープチェーンであり、変数の検索時はこのリストを末尾から先頭に順にたどって プロパティを探していく。 スコープチェーンに同名のものが見つからなかった場合は次の動作を行う。

[[Scope]]は関数が生成されたときに初期化されて、そのときのスコープチェーンの 内容を引き継ぐ。関数が定義された時とは、関数定義の場合はGlobalオブジェクトや Activationオブジェクトの初期化時で、関数リテラルの場合はその式が評価された時である。 該当する関数を実行する際に、Activationオブジェクトを新たに作成して、 リストの末尾に追加する。

(例)
  var a = 1;
  function test() {
     var b = 2;
  }
  test();
手書きの説明
(例)
  var a = 1;
  funciton test1() {
    var b = 2;
    test2();
  }
  function test2() {
    var c = 3;
  }
  test1();
手書きの説明
(例)
  function outer() {
    var a = 0;
    return function() {
      console.log(a++);
    }
  }
  var test1 = outer();
  test1();    // 「0」
  test1();    // 「1」
  var test2 = outer();
  test2();    // 「0」
手書きの説明

Chrome を起動して http://nw.tsuda.ac.jp/class/cg/all/javascript/ex2-50.html にアクセスせよ。画面の上のメニューから「その他のツール」-->「デベロッパーツール」-->「Console」を選択し、Chromeの画面の右半分にJavaScriptコンソールを表示し、 コンソールへの出力(ロギング)を確認すること。

ex2-50.html
<html lang="ja">
<head>
  <meta charset="utf-8" />
</head>
<body>
Sample 2-50
  <script type="text/javascript">
  //<![CDATA[
var test1 = outer();
test1();    // 「0」
test1();    // 「1」
var test2 = outer();
test2();    // 「0」

function outer() {
  var a = 0;
  return function() {
    console.log(a++);
  }
}
  //]]>
  </script>
</body>
</html>