JavaScriptでクラスを定義する方法を勝手に定義2 – jsからnewを消し去る

phiary に引っ越しました. 毎日プログラミングやWebに関する情報を発信しています! RSS 登録してたまに覗いたり, tweet やハテブして拡散してもらえると幸いです.

Pocket

newが「JavaScript: The Good Parts ―「良いパーツ」によるベストプラクティス
というとても有名な本で、悪しきパーツとして紹介されていた。

たしかにJavaScriptにおいてnewは他の言語にあわせて無理やり追加した感丸出しである。
GCもあるしってことで、前回書いたクラスを定義new を消し去る処理を加えてみた。

調べてみたところ、もうすでにnewを消し去っている人がいたので
その中からよくあるパターンを抜粋。

var Vector2 = function() {
    var temp = function(){}
    
    temp.prototype = Vector2.prototype;
    
    return new temp;
}

しか~しこれだと毎回プロトタイプを代入して生成しなければならなくなる。
複数の引数にも対応できない!!

そこでより調べてみたところかなり身近なところに参考になるプログラムがあった!!
それはjQueryだ!!

jQueryが new を使わずにjQueryオブジェクトの生成をしているのは周知の事実だろう。
そこでソースを読んでみると意外とシンプルな方法でnewを排除していた。

なのでjQueryを参考に、複数の引数にも対応させて作ってみた。

Script


/**
 * @author phi
 */

var TM = {};

/**
 * Vector2 class
 */
(function(namespace){
    // ネームスペース
    // 指定がなかった場合デフォルトでグローバル(window)に追加する
    namespace = namespace || window;
    
    // コンストラクタ
    var Vector2 = namespace.Vector2 = function(x, y){
        var temp = new Vector2.prototype.create();
        
        // 引数をまんま渡してinit関数を呼び出す
        return Vector2.prototype.init.apply(temp, arguments);
    }
    
    // プロトタイプ
    // ここでメンバ変数(プロパティ)やメンバ関数を宣言&定義する
    Vector2.prototype = {
        // --------------------------------
        // property list
        // --------------------------------
        x:0,
        y:0,
        
        // --------------------------------
        // function list
        // --------------------------------
        // 生成関数
        // 実際にはこれを生成する
        create:function(){
            return this;
        },
        // 初期化
        init:function(x, y){
            this.x = x || 0;
            this.y = y || 0;
            
            return this;
        },
        // 長さ
        length:function(){
            return Math.sqrt(this.x*this.x+this.y*this.y);
        },
        // 文字列に変換
        toString:function(){
            return '(' +this.x+', '+this.y+')';
        },
        // エレメントに書き込む
        writeToElement:function(element) {
            element.innerHTML += this.toString() + "<br />";
        },
        // パラメータをアラート
        alert:function(){
            alert(this.toString());
        }
    }
    // createオブジェクトでVector2のメンバを使えるようにする
    Vector2.prototype.create.prototype = Vector2.prototype;
    
    // スタティックなメンバを定義
    // オブジェクトを生成しなくても使える変数や関数です
    Vector2.className   = "Vector2";
    Vector2.version     = "1.0.0";
    Vector2.detail      = 
        "ためしに作ってみたVector2クラスです。\
        あくまでクラス定義のサンプルなので、\
        normalizeがないとか内積、外積は??\
        といったツッコミはなしでお願いしますw";
        
})(TM);

// 下記を書けば"TM."を付けなくても使えるようになる
// window.Vector2 = TM.Vector2;

    

実際に生成しているのは、Vector2.prototype.createだが、
Vector2.prototype.createがVector2を継承しているのでまったく同じように使える。

それと、あえて初期化処理をinitに分けてapplyで呼び出すことで複数の引数にも対応してみた。

Sample


<!DOCTYPE html>
    
<html lang="ja">
    <head>
        <meta charset="utf-8">
        <title>Define class definition non new</title>
        
        <script type="text/javascript" src="script.js"></script>
        <script type="text/javascript">
            window.onload = function(){
                var vector_list = [];   // 配列
                
                // ベクトル2クラスを生成
                for (var i=0; i<32; ++i) {
                    // new なしで生成
                    vector_list.push(TM.Vector2(Math.random()*100, Math.random()*100));
                }
                
                // クラス名を表示
                var class_name_element = document.getElementById("class-name");
                class_name_element.innerHTML += TM.Vector2.className;
                
                // クラスの情報を表示
                var class_info_element = document.getElementById("class-info");
                class_info_element.innerHTML += TM.Vector2.detail;
                
                // エレメントを取得
                var msg_element = document.getElementById("message");
                
                // x,yを表示しまくる
                for (var i=0; i<vector_list.length; ++i) {
                    msg_element.innerHTML+=(i+1)+". ";
                    vector_list[i].writeToElement(msg_element);
                }
            }
        </script>
    </head>
    
    <body>
        <h1>Define class definition non new</h1>
        <p>
            勝手にクラス定義の方法を定義してみました。
            ついでに new も消し去ってみました。
        </p>
        
        <h2 id="class-name"></h2>
        <p id="class-info"></p>
        <div id="message"></div>
        
        <hr />
        <a href="/">Home</a>
    </body>
</html>
    

実行結果

ちなみにこの書き方だと内部でthisを使っていないので
newを付けようが付けまいが同じ挙動になる、、、ハズです。

TRACK BACK URL

POST COMMENT

メールアドレスが公開されることはありません。

COMMENT