JavaScriptに関する個人的なメモ

JavaScriptと他のプログラミング言語との違い

このメモは、JavaScriptと、それ以外のプログラミング言語との違いを解説したものです。
他の方の解説を読むと、共通点が強調されているので、このメモでは、逆に、違う点を調べていこうというわけです。
個人的なメモなので、間違いがあります。見つけたら、すぐに訂正します。
C言語C++PerlJavaPHPRubyなど、JavaScript以外の言語を習得している人を念頭においています。コメントは/*と*/との間に挟みます。

リテラル

JavaScriptにはクラスはありません。6種類のリテラルを使ってオブジェクトを作ります。
リテラルは便利なのでよく使われます。

  var a = {},              /*オブジェクトリテラル*/
      b = 12,              /*数値リテラル*/
      c = "Hello, World!", /*文字列リテラル*/
      d = /a+b*c?/g,       /*正規表現リテラル*/
      e = [],              /*配列リテラル*/
      f = function(){};    /*関数リテラル*/

メソッド

オブジェクトは、メソッドを呼び出すことが可能となります。このメソッドを使って、JavaScriptでは処理を進めていきます。

メソッドの定義

オブジェクトリテラル、関数リテラルとコロンを組み合わせて定義します。

  var a = {
             hoge: function() {},
             hogehoge: function() {}
  };
メソッドの呼び出し

メソッドの呼び出しはドットを記して、メソッド名に()を付ければOKです。

  a.hoge();

変数を使わずに、リテラルとドットを使って、直接メソッドを呼び出すことも可能です。

  "abcdefghijk".indexOf("c"); /*2を返す*/
メソッドの書き換え

メソッドは動的に書き換えることができます。また、後から新しく追加することも可能です。

  a.hoge = function(){};
  a.ne = function(){};

関数呼び出し

関数リテラルを通じて、関数を呼び出すパターンは5通りあります (注1)。それぞれ関数を実行することができます。

  var f = function(){},
      obj = {
              hoge : function(){}
            };
  /*関数呼び出しパターン*/
  f();

  /*apply (call) 呼び出しパターン*/
  f.apply();
  f.call();

  /*メソッド呼び出しパターン*/
  obj.hoge();

  /*即時関数*/
  (function(){})();

  /*コンストラクタ呼び出しパターン*/
  new f();

var

関数の内部か、外部のどこかでvarを指定しない限り、変数はグローバル変数として扱われます。
関数を呼び出す直前に、関数ブロック内で、varで指定された変数はundefinedとなります。そのため、以下のような奇妙なことが起きます。

  hoge = 12; /*hogeグローバル変数*/
  var f = function() {
            hoge; /*下にvarがついているため、12ではなくて、undefinedとなる*/
            
            var hoge;
      };
 f();

 var df = function() {
            var hoge = hoge; /*varの初期化が先に実行されるので、これも12ではなくて、undefined*/
      };
  df();

また、ブロックスコープはありませんので、ブロックの外から変数にアクセス可能です。

  var f = function() {
            for (var i=0;i < 12; i++) {}
            i; /*このとき、iは11*/
      };
 f();

クロージャ

プライベート変数はありません。クロージャを使って、プライベート変数の代わりとします。
クロージャを使えば、内部の関数は、外側の関数で定義された変数にアクセスすることができます。
以下のコードでは、メソッドでしかプライベート変数にアクセスできません。また、that = thisというのは、コンストラクタ呼び出しパターンの場合、メソッドの参照引渡しでthisに問題が起きるため、回避する対策を打っています。

  var F = function(){
            var that = this,
                a; /*プライベート変数として使う*/
            that.getA = function() {
                          return a;
            };
            that.setA = function(n) {
                          a = n;
            };
            that.hoge = function() {
                          return that.getA();
            };
  };
  var obj = new F();
  obj.setA(1);
  obj.getA(); /*1を返す*/
  obj.setA(2);
  obj.getA(); /*2を返す*/
  obj.hoge(); /*2を返す*/
継承

さらにapply呼び出しパターンを使って、シンプルな継承も実現できます。
継承させたいコンストラクタ関数を、Super1、Super2とSuper3と名前を付けておきましょう。それらのメソッドをすべて、Child関数に継承させます。

  var Super1 = function(){
            var that = this,
                a; /*プライベート変数として使う*/
            that.getA = function() {
                          return a;
            };
            that.setA = function(n) {
                          a = n;
            };
            that.hoge = function() {
                          return that.getA();
            };
  },
  Super2 = function(){
            var that = this;
            that.ho = function(){};
  },
  Super3 = function(){
            var that = this;
            that.h = function(){};
  },
  Child = function() {
            var that = this;
            /*多重継承を始める*/
      Super1.apply(that, arguments);
      Super2.apply(that, arguments);
      Super3.apply(that, arguments);
            that.n = function() {
                       return that.hoge();
            };
  };
  var obj = new Child();
  obj.setA(2);
  obj.hoge(); /*2を返す*/
  obj.ho();
  obj.h();
  obj.n();    /*2を返す*/

結論

  1. JavaScriptはクラスもないし、オブジェクト指向言語ではないけど、継承など同じことはできます
  2. 開発したいときは、ややこしさを避けるために、ライブラリを使うのがベスト

続き

JavaScriptの多重継承とオーバライド - JavaScriptの個人的なメモ (その2)

http://d.hatena.ne.jp/dhrname/20121004
プロトタイプチェーンについては機会があればメモとして整理しておきたいです。

参照

注1

Douglas Crockford 著 水野貴明 訳「JavaScript: The Good Parts 『良いパーツ』によるベストプラクティス」 P32より

追記 (2012年10月13日)

JavaScriptはプロトタイプベースのオブジェクト指向言語とする考えがネットで多く見られましたので、オブジェクト指向に関する記述を削除しておきました。