LoginSignup
38

More than 5 years have passed since last update.

JavaScriptでのクラスの書き方

Last updated at Posted at 2016-05-20

JavaScriptでもクラスな考え方をしたい

「JavaScriptにおいて、どうクラスを書くか?」の問いに
「コレだ!」ってのがなかなか見つからなかったが、やっと自分の中で
こう書くとシックリくる」ってのが見つかったのでメモ、って話。

JavaScript言語では「クラス」という概念は無い(※1)。
無いが、「Google流 JavaScript におけるクラス定義の実現方法(※2)」
とかでも「クラス」って表現は普通に使われている。
さて、疑似的に「クラス」を実装するにはどう書けばよいか?

クラスの記述方針

以下のような方針でクラスを書くと、しっくり来た。

  1. グローバル空間で「var クラス名 = function(){}」にて『クラスを定義』する。
  2. 静的メンバ変数のprivateスコープ化は、クロージャーを使って実現する。
  3. 動的メンバのprivateスコープ化は、『諦める』。

この方針であれば、ある程度のprivate化を行いながらも、
メソッドはprototypeで定義できるので、「メモリへの負荷が無い」
っていうプロトタイプベースの恩恵も享受できる(※3)。

サンプルコード

具体例としては、以下のようなコードになる。
なお、jqueryとjquery.cookie.js(※4)を利用している。

class_cookie.html
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<script type='text/javascript' src='./jquery-1.12.3.js'></script>
<script type='text/javascript' src='./jquery.cookie.js'></script>
<script>

var CookieBind = function( cookieName ){
    // コンスタラクタ
    // ⇒グローバル変数で定義する。
    //
    this._cookieName = cookieName;

    // このタイミングでは未定義のメソッド、も指定可能。
    // ⇒「this」は実行時解決されるから。
    this.itsLoadedStr = this.load(); 
};
(function(){
    //
    // 続いて、prototypeを用いてメソッドを定義
    // ⇒即時関数の中で行うことで、静的praivate変数/メソッドを実装可能。
    //  クロージャーの仕組みを利用している。
    //
    // ⇒動的なpraivate変数は諦める。
    //  (メソッドは、適宜thisを渡すことで動的な実装は可能)。
    //
    // ⇒JavaScriptの場合は、メソッドは「静的な枠」+「動的なメンバ変数」で実装する。
    //
    // ※prototype内で function(){} 定義するときは、thisに注意。
    //  ⇒ function(){ の内部 } では、thisは呼び出し元になり、このクラスインスタンスを指さない。
    //  ⇒ function() の親側で var self = this; して、function(){ の内部 } では、
    //    self.~の形式で呼ぶこと(selfはクロージャーになる)。
    //
    var COOKIE_OPTION = { expires: 7 }; // 静的プライベート変数

    CookieBind.prototype.save = function( str ){
        $.cookie( this._cookieName, str, COOKIE_OPTION );
    };
    CookieBind.prototype.load = function(){
        return $.cookie( this._cookieName );
    };
    CookieBind.prototype.getValue = function(){
        return this.itsLoadedStr;
    }
    CookieBind.prototype.getCookieName = function(){
        return this._cookieName;
    }
    // prototype = {}; は禁止。いろいろ破壊するので、扱いが面倒。
}());




// クラス継承はこちら。
var CookieBind2Input = function( cookieName, id_input, id_button ){
    var self = this;

    CookieBind.call( this, cookieName ); // 継承元のコンスタラクタを明示的に呼び出す。
    this._idInput = id_input;
    this._idButton = id_button;

    this.load2Input();
    $("#" + this._idButton).click( function(){
        self.saveByButton(); // この位置は、thisが異なるので注意。
    });
};
// 親のメソッドを「継承」する。
CookieBind2Input.prototype = Object.create( CookieBind.prototype ); 
// サブクラスでのメソッド追加も、同様に即時関数内で行う。
(function(){
    // 明示的にインスタンスを渡すことで、privateな動的メソッドを実装可能。
    var privateFunc = function( instance, value ){ 
        var str = value + "\r\nをcookie名 ";
        str += instance._cookieName; // 親クラスの動的メンバにアクセス。
        str += " に保存しました!";
        alert( str );
    };

    CookieBind2Input.prototype.load2Input = function(){
        var value = this.getValue();
        if( value ){
            $("#" + this._idInput).val( value );
        }
    };
    CookieBind2Input.prototype.saveByButton = function(){
        var value = $("#" + this._idInput).val();
        if( value.length > 0 ){
            this.save( value );
            privateFunc( this, value );
        }
    };
    CookieBind2Input.prototype.del = function(){
        this.save( null );
    }
}());


// 実行例
$(document).ready(function(){
    var text_cookie1 = new CookieBind( "key-sample1" );
    var text_cookie2 = new CookieBind2Input( "key-sample2", "id_text2", "id_button2" );
    var text_cookie3 = new CookieBind2Input( "key-sample3", "id_text3", "id_button3" );

    if( text_cookie1.getValue() ){
        $("#id_text1").val( text_cookie1.getValue() );
    }
    $("#id_button1").click( function(){
        var text = $("#id_text1").val();
        text_cookie1.save( text );
        alert( text + "\r\nをcookieに保存しました" );
    })

    $("#id_clear").click( function(){
        text_cookie1.save( null );
        text_cookie2.save( null ); // スーパークラスのメソッド呼んでみる。
        text_cookie3.del();
        alert( "cookieを削除しました" );
    })
});

</script>
</head>
<body>
<form>
    <input id="id_text1"   type="text" style="width: 128;"></input>
    <input id="id_button1" type="button" value="保存1"></input>
    <br>
    <br>
    <input id="id_text2"   type="text" style="width: 128;"></input>
    <input id="id_button2" type="button" value="保存2"></input>
    <br>
    <br>
    <input id="id_text3"   type="text" style="width: 128;"></input>
    <input id="id_button3" type="button" value="保存3"></input>
    <br>
    <br>
    <input id="id_clear"   type="button" value="クッキー削除"></input>
</form>
</body>
</html>

補足

※1:ECMAScript5前提。IE11でも動くように。

※2:Google流 JavaScript におけるクラス定義の実現方法
   http://www.yunabe.jp/docs/javascript_class_in_google.html
   ⇒ ほぉ!と思ったが、private化を「名前の末尾に _ を付ける」
    名前規約で対応している所が、モヤモヤする。

※3:コンスタラクタでreturn { mehotd1 : funcion() }形式で
   メソッド定義すれば、praivateメンバ/メソッド利用し放題なのだが、
   それだとインスタンス生成(new)の度に、全てのメソッドの生成コスト
   が掛かる。せっかくのプロトタイプベースのJavaScriptでソレは、
   モヤモヤとする。 

※4:jquery.cookie.js は「cookieを手軽に扱えるようになるjQueryのプラグイン」。
   「jquery.cookie.jsの使用方法メモ」などを参照のこと。
   http://cly7796.net/wp/javascript/plugin-jquery-cookie/

※5:ちな、JavaScript のオブジェクト指向プログラミングの考え方は、
   公式のMozilla Developer Network のページが普通に分かり易くて
   驚いた。このページは、適宜読み返したいと思う。
   https://developer.mozilla.org/ja/docs/Web/JavaScript/Introduction_to_Object-Oriented_JavaScript

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
38