JavaScript Diary

今年も残すところあと一日 [ 2001/12/31 ]

この一年 JavaScript を掘り進めていったことにより、僕の JavaScript に対する考え方は非常に変わりました。
特にJavaScriptサイドのメインであるユーザ定義オブジェクト(クラス)作成に関してはあらゆる意味でレベルが上がったのではないかと自負しています。

今年最後の更新、総括として僕のユーザ定義オブジェクト作成におけるコーディングスタイルを示し、今年を締めくくりたいと思います。

その例として(まだ作成途中ですが)、DHTMLのイベントをクロスブラウザで扱うクラス JEventクラス を挙げたいと思います。

// JEventクラスの宣言(関数名は class__Class__ で統一)
function class__JEvent__(){

// クラス名を関数内で保持しておく(文字列)
var classId = arguments.callee.toString().match( /function (\w+)/ )[1].match( /__(\w+)__/ )[1];

// private インスタンス変数 event
// Object.privateId に関しては 2001/09/26 の後半を参考
var $event = Object.privateId();

// クロスブラウザのためのフラグ
var ie = document.all !== void 0 ;                      // InternetExplorer
var nn = document.layers !== void 0 ;                   // NetscapeNavigator4
var gk = navigator.userAgent.indexOf( "Gecko" ) != -1 ; // Netscape6, Mozilla

// JEventクラスコンストラクタ(但し、ただの関数としても使用可能)
// 関数として使用された場合、イベントオブジェクトを返す
// 
// class    : JEvent( void );
// function : Event JEvent( void );
var F = window[ classId ] = function(){
    
    if( arguments.callee === this.constructor ){ // クラスとしての処理
        
        // この関数を使用しイベントオブジェクトを取得する
        // 取得したオブジェクトを privateインスタンス変数 event に設定する
        var e = this[$event] = arguments.callee();
        
        this.type = e.type ; // イベントのタイプ
        
        this.button = ie ? e.button : e.which ; // ボタン
        this.button1 = this.button == 1 ;                                // Primary button
        this.button2 = this.button == ( ie ? 2 : 3 );                    // Secondary button
        this.button3 = !nn ? ( this.button == ( ie ? 4 : 2 ) ) : false ; // Scroll button (NN4は常にfalse)
        
        this.altKey = !nn ? e.altKey : Boolean( e.modifiers&1 );
        this.ctrlKey = !nn ? e.ctrlKey : Boolean( e.modifiers&2 );
        this.shiftKey = !nn ? e.shiftKey : Boolean( e.modifiers&4 );
        
        this.keyCode = ie ? e.keyCode : ( nn ? e.which : e.charCode );
        
        // NN4 の場合、functionリテラルでコンストラクタを書くと
        // constructorプロパティに値が設定されないためここで強制的に設定しておく
        // prototype につら下げるのは不可(関数としての機能が無くなる)
        if( document.layers !== void 0 ) this.constructor = F ;
        
    }else{ // 関数としての処理
        
        // イベントオブジェクトを返す(前回の日記を参考)
        if( ie ){
            return window.event ;
        }else{
            for(var f=arguments.callee.caller;f!=null;f=f.caller){
                var e = f.arguments[0];
                if( e !== void 0 && typeof e == 'object' && e.toString() == '[object Event]' ) return e ;
                if( f.caller == f ) break ;
            }
            return null ;
        }
        
    }
    
};

// クラス変数(の例)

F.MOUSEDOWN =  1 ;
F.MOUSEUP   =  2 ;
F.MOUSEOVER =  4 ;
F.MOUSEOUT  =  8 ;
F.MOUSEMOVE = 16 ;

/************************* JEventクラスのメソッド定義ここから *************************/

var FP = F.prototype ; // 簡略化のため

// 押されたキー1文字を返す
// char getKey( void );
FP.getKey = function(){
    var c = this.keyCode ;
    if( this.type == 'keypress' ){
        return String.fromCharCode( c );
    }else{
        if( ie ){
            // ここは長くなるので手抜き
            return String.fromCharCode( c );
        }else{
            return String.fromCharCode( c );
        }
    }
};

// イベントの浮上を制御(引数が false のとき浮上停止)
// void setBubble( void );
// void setBubble( boolean v );
FP.setBubble = function( v ){
    if( !nn ) this[$event].cancelBubble = !Boolean(v);
};

// イベントが発生した座標を取得

// int getAbsoluteX( void );
FP.getAbsoluteX = function(){
    return ie ? this[$event].clientX-2+document.body.scrollLeft : this[$event].pageX ;
};
// int getAbsoluteY( void );
FP.getAbsoluteY = function(){
    return ie ? this[$event].clientY-2+document.body.scrollTop : this[$event].pageY ;
};
// Dimension getAbsolutePosition( void );
FP.getAbsolutePosition = function(){
    return new Dimension( this.getAbsoluteX(), this.getAbsoluteY() );
};

// イベントが発生した座標をブラウザ表示可能領域の左上端を原点として取得

// int getRelativeX( void );
FP.getRelativeX = function(){
    if( ie ){
        return this[$event].clientX-2 ;
    }else if( nn ){
        return this[$event].pageX-window.pageXOffset ;
    }else{
        return this[$event].clientX ;
    }
};
// int getRelativeX( void );
FP.getRelativeY = function(){
    if( ie ){
        return this[$event].clientY-2 ;
    }else if( nn ){
        return this[$event].pageY-window.pageYOffset ;
    }else{
        return this[$event].clientY ;
    }
};
// Dimension getRelativePosition( void );
FP.getRelativePosition = function(){
    return new Dimension( this.getRelativeX(), this.getRelativeY() );
};

// HTMLElementで発生したイベントの座標を親要素の左上端を原点として取得

// int getElementX( void );
FP.getElementX = function(){ return ie ? this[$event].offsetX : this[$event].layerX ; };
// int getElementY( void );
FP.getElementY = function(){ return ie ? this[$event].offsetY : this[$event].layerY ; };
// Dimesnion getElementPosition( void );
FP.getElementPosition = function(){
    return new Dimension( this.getElementX(), this.getElementY() );
};

// イベントの発生した座標をディスプレイの左上端を原点として取得

// Dimension getScreenPosition( void );
FP.getScreenPosition = function(){
    return new Dimension( this[$event].screenX, this[$event].screenY );
};

// 文字列型参照
// string toString( void );
FP.toString = function(){ return classId+" : "+this[$event]; };

/************************* JEventクラスのメソッド定義ここまで *************************/

} class__JEvent__();
// 関数の呼び出し
// ここで初めて JEventクラス に関するメソッド等の初期設定がされる

改めて見ると凄い書き方ですね ^^);
この書き方は JavaScript1.2 が搭載されていることが大前提でそれ以前のバージョンだとパースする時点でエラーが出ます。

最近は function class__SubClass_extends_SuperClass__(){ ... と書けば継承できるような雛型を考えています

では、皆さんよいお年を!!