天天看點

JS類的封裝及實作代碼【轉】

本文轉自:http://www.jb51.net/article/21197.htm

js并不是一種面向對向的語言, 沒有提供對類的支援, 是以我們不能像在傳統的語言裡那樣 用class來定義類, 但我們可以利用js的閉包封裝機制來實作js類, 我們來封裝一個簡的Shape類.
           

1. 定義js類

js并不是一種面向對向的語言, 沒有提供對類的支援, 是以我們不能像在傳統的語言裡那樣 用class來定義類, 但我們可以利用js的閉包封裝機制來實作js類, 我們來封裝一個簡的Shape類.
           

代碼如下:

function ShapeBase() {
    this.show = function(){
        alert("ShapeBase show");
    };
    this.init = function(){
        alert("ShapeBase init");
    };
}
           

這個類裡定義了兩個方法:show和init, 需要注意的是這裡用到了this來聲明, 而不是var, 因為用var是用來定義私有方法的.

另外, 我們還可以用prototype屬性來定義Shape的方法.

代碼如下:

ShapeBase.prototype.show=function(){
    alert("ShapeBase show");
}
ShapeBase.prototype.init=function(){
    alert("ShapeBase init");
}
           

上面這種寫法看起來不太直覺,我們可以将所有的方法寫在一起.

代碼如下:

ShapeBase.prototype={
    show:function(){
        alert("ShapeBase show");
    },
    init:function() {
        alert("ShapeBase init");
    }
};
           

現在, 類是寫好了, 讓我們寫個js來測試下, 看看結果是不是跟我們想象的一樣呢?

代碼如下:

function test(src){
    var s=new ShapeBase();
    s.init();
    s.show();
}
           

看到了吧, 其調用方式和C#一模一樣, 而結果也如我們所料.

到目前為止, 我們學會了如何建立js的類了, 但還隻是執行個體方法,要是實作跟C#中的靜态方法要怎麼做呢?

其實, 實作js的靜态方法很簡單, 看下面如何實作:

代碼如下:

//靜态方法
ShapeBase.StaticDraw = function(){
    alert("method draw is static");
}
           

2. 實作JS類抽象和繼承

同樣, js中也不支援類繼承機制,但我們可以通過将父類prototype中的成員方法複制到子類的prototype中來實作.
           

和類的繼承一樣,JavaScript也沒有任何機制用于支援抽象類.但利用JavaScript語言本身的性質.可以實作自己的抽象類.

首先來看看js中的虛方法, 在傳統語言中虛方法是要先定義的, 而包含虛方法的類就是抽象類,不能被執行個體化,而在JavaScript中,虛方法就可以看作該類中沒有定義的方法,但已經通過this指針使用了.

和傳統面向對象不同的是,這裡虛方法不需經過聲明,而直接使用了, 并且類也可以被執行個體化.

先定義object的extend方法, 一個為靜态方法,一個為執行個體方法, 這兩個方法用于實作繼承的prototype複制

代碼如下:

Object.extend = function(destination, source) {
    for (property in source) {
        destination[property] = source[property];
    }
    return destination;
}
Object.prototype.extend = function(object) {
    return Object.extend.apply(this, [this, object]);
}
           

接下來我們實作一個繼承類Rect, 這裡先用一種簡單的方法來實作。

代碼如下:

function Rect() {
}
Rect.prototype = ShapeBase.prototype; //隻這一句就行了
//擴充新的方法
Rect.prototype.add=function() {
    alert("Rect add");
}
           

這種方法不能用于重寫,如果改變了show方法, ShapeBase的show也會指向同一函數可能是由于prototype指派隻是簡單的改變指向位址.

如果上面也定義了:

Rect.prototype.show=function() {
    alert("Rect show");
}
           

那麼執行結果如下:

function test(){
    var s=new ShapeBase();
    s.show(); //結果:Rect show
    var r=new Rect();
    r.show(); //結果:Rect show
    r.add();
}
           

我們再使用object.extend實作繼承, 并實作一個oninit虛方法, 修改ShapeBase如下:

代碼如下:

ShapeBase.prototype={
    show:function(){
        alert("ShapeBase show");
    },
    initialize:function () {
        this.oninit();
    }
};
           

實作Rect類繼承.

代碼如下:

Rect.prototype=(new ShapeBase).extend({
    //添加新的方法
    add:function() {
        alert("Rect add");
    },
    //使用這種方法可以重寫show方法
    show:function() {
        alert("Rect show");
    },
    //實作虛方法
    oninit:function() {
        alert("Rect oninit");
    }
})
           

現在我們的類寫好了, 測試下看看:

代碼如下:

function test(src){
    ShapeBase.StaticDraw();
    var s=new ShapeBase();
    s.show(); //alert("ShapeBase show")
    var r=new Rect();
    r.show(); //alert("Rect show")
    r.add();
    r.initialize(); //alert("Rect oninit")
}
           

另外,在網上看到一篇用專門的對象來建立類,代碼如下:

代碼如下:

/*
*對象屬性複制方法,很多庫都有實作,如PrototypeJS裡面的extend和Ext裡面的Ext.apply
*/
function extend(des, src) {
    if (!des)
    des = {};
    if (src) {
        for (var i in src) {
            des[i] = src[i];
        }
    }
    return des;
}
var CC = {}; //全局變量

/*
*create 用于建立類
*/
CC.create = function(superclass, constructor){
    var clazz = (function() {
        this.initialize.apply(this, arguments);
    });
    //如果無參數,直接傳回類.
    if(arguments.length == )
    return clazz;
    //如果無父類,此時constructor應該為一個純對象,直接複制屬性傳回.
    if(!superclass){
        extend(clazz.prototype, constructor);
        return clazz;
    }
    var absObj = clazz.prototype,
    sprPropty = superclass.prototype;
    if(sprPropty){
        //用于通路父類方法
        clazz.superclass = sprPropty;
        extend(absObj, sprPropty);
        //調用屬性構造函數建立屬性,這個是實作關鍵.
        extend(absObj, constructor(sprPropty));
        // 子類執行個體直接通過obj.superclass通路父類屬性,
        // 如果不想造成過多引用,也可把這句注釋掉,因為多數時候是沒必要的.
        absObj.superclass = sprPropty;
        //
        clazz.constructor = constructor;
    }
    return clazz;
}

/*
*建立一個動物類
*/
var Animal = CC.create(null, {
    //屬性
    footprint : '- - - - - - =',
    //類初始化方法,必須的,當用 new 生成一個類時該方法自動被調用,參見上定義.
    initialize : function(options){
        extend(this, options);
        alert('Animal initialize method is called.');
    },
    eat : function(){
        alert('Animal eat method is called.');
    },
    move : function(){
        alert('I am moving like this '+ this.footprint +' .');
    }
});

/*
*建立一個Duke類
*/
var Duke = CC.create(Animal, function(superclass){
    //在這可以定義一些類全局靜态資料,該類每個執行個體都共享這些資料.
    //計算執行個體個類,包括派生類執行個體.
    var static_instance_counter = ;
    function classUtilityFuncHere(){ }
    //傳回類具體屬性.
    return {
        //重寫初始化方法
        //@override
        initialize : function(options) {
            alert('Initializing Duke class..');
            //調用父類初始化,這種方法比一般其它庫的要簡潔點吧,可以不管父類是什麼.
            superclass.initialize.call(this, options);
            //做一些子類喜歡做的事.
            alert('Duke initialize method is called.');
            //讀取或修改類靜态屬性
            static_instance_counter++;
        },
        //重寫move方法,增加Duke自己的移動方式.
        move : function(){
            this.footprint = this.footprint + 'zzzzzzzz';
            superclass.move.call(this);
        },
        //重寫eat方法,注意,裡面不調用父類方法,即父類eat被覆寫了.
        eat : function(){
            alert('Duke is eating..');
        },
        //新增一個say方法,顯示目前已經初始化的Duke類執行個體數量.
        say : function(){
            alert('the number of Duke instances is '+static_instance_counter);
        }
    };
});

var DukeChild = CC.create(Duke, function(superclass){
    return {
        move : function(){
            this.footprint = this.footprint + '++++++++++++=';
            superclass.move.call(this);
        },
        say : function(){
            alert(this.msg || '');
        }
    };
});
(function test() {
    var animal = new Animal();
    animal.eat();
    animal.move();
    var dukeA = new Duke();
    dukeA.eat();
    dukeA.move();
    dukeA.say();
    var dukeB = new Duke();
    dukeB.eat();
    dukeB.move();
    dukeB.say();
    var dukeC = new DukeChild({msg : 'I am a child of duke.'});
    dukeC.move();
    dukeC.say();
})();
           

繼續閱讀