JS類的封裝及實(shí)現(xiàn)代碼
更新時間:2009年12月02日 00:21:39 作者:
js并不是一種面向?qū)ο虻恼Z言, 沒有提供對類的支持, 因此我們不能像在傳統(tǒng)的語言里那樣 用class來定義類, 但我們可以利用js的閉包封裝機(jī)制來實(shí)現(xiàn)js類, 我們來封裝一個簡的Shape類.
1. 定義js類
js并不是一種面向?qū)ο虻恼Z言, 沒有提供對類的支持, 因此我們不能像在傳統(tǒng)的語言里那樣 用class來定義類, 但我們可以利用js的閉包封裝機(jī)制來實(shí)現(xiàn)js類, 我們來封裝一個簡的Shape類.
function ShapeBase() {
this.show = function()
{
alert("ShapeBase show");
};
this.init = function(){
alert("ShapeBase init");
};
}
這個類里定義了兩個方法:show和init, 需要注意的是這里用到了this來聲明, 而不是var, 因?yàn)橛胿ar是用來定義私有方法的.
另外, 我們還可以用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");
}
};
現(xiàn)在, 類是寫好了, 讓我們寫個js來測試下, 看看結(jié)果是不是跟我們想象的一樣呢?
function test(src){
var s=new ShapeBase();
s.init();
s.show();
}
看到了吧, 其調(diào)用方式和C#一模一樣, 而結(jié)果也如我們所料.
到目前為止, 我們學(xué)會了如何創(chuàng)建js的類了, 但還只是實(shí)例方法,要是實(shí)現(xiàn)跟C#中的靜態(tài)方法要怎么做呢?
其實(shí), 實(shí)現(xiàn)js的靜態(tài)方法很簡單, 看下面如何實(shí)現(xiàn):
//靜態(tài)方法
ShapeBase.StaticDraw = function()
{
alert("method draw is static");
}
2. 實(shí)現(xiàn)JS類抽象和繼承
同樣, js中也不支持類繼承機(jī)制,但我們可以通過將父類prototype中的成員方法復(fù)制到子類的prototype中來實(shí)現(xiàn).
和類的繼承一樣,JavaScript也沒有任何機(jī)制用于支持抽象類.但利用JavaScript語言本身的性質(zhì).可以實(shí)現(xiàn)自己的抽象類.
首先來看看js中的虛方法, 在傳統(tǒng)語言中虛方法是要先定義的, 而包含虛方法的類就是抽象類,不能被實(shí)例化,而在JavaScript中,虛方法就可以看作該類中沒有定義的方法,但已經(jīng)通過this指針使用了.
和傳統(tǒng)面向?qū)ο蟛煌氖牵@里虛方法不需經(jīng)過聲明,而直接使用了, 并且類也可以被實(shí)例化.
先定義object的extend方法, 一個為靜態(tài)方法,一個為實(shí)例方法, 這兩個方法用于實(shí)現(xiàn)繼承的prototype復(fù)制
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]);
}
接下來我們實(shí)現(xiàn)一個繼承類Rect, 這里先用一種簡單的方法來實(shí)現(xiàn)。
function Rect() {
}
Rect.prototype = ShapeBase.prototype; //只這一句就行了
//擴(kuò)充新的方法
Rect.prototype.add=function() {
alert("Rect add");
}
這種方法不能用于重寫,如果改變了show方法, ShapeBase的show也會指向同一函數(shù)可能是由于prototype賦值只是簡單的改變指向地址.
如果上面也定義了:
Rect.prototype.show=function() {
alert("Rect show");
}
那么執(zhí)行結(jié)果如下:
function test(){
var s=new ShapeBase();
s.show(); //結(jié)果:Rect show
var r=new Rect();
r.show(); //結(jié)果:Rect show
r.add();
}
我們再使用object.extend實(shí)現(xiàn)繼承, 并實(shí)現(xiàn)一個oninit虛方法, 修改ShapeBase如下:
ShapeBase.prototype={
show:function()
{
alert("ShapeBase show");
},
initialize:function () {
this.oninit();
}
};
實(shí)現(xiàn)Rect類繼承.
Rect.prototype=(new ShapeBase).extend({
//添加新的方法
add:function() {
alert("Rect add");
},
//使用這種方法可以重寫show方法
show:function() {
alert("Rect show");
},
//實(shí)現(xiàn)虛方法
oninit:function() {
alert("Rect oninit");
}
})
現(xiàn)在我們的類寫好了, 測試下看看:
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")
}
另外,在網(wǎng)上看到一篇用專門的對象來創(chuàng)建類,代碼如下:
//
//對象屬性復(fù)制方法,很多庫都有實(shí)現(xiàn),如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 用于創(chuàng)建類
//
CC.create = function(superclass, constructor){
var clazz = (function() {
this.initialize.apply(this, arguments);
});
//如果無參數(shù),直接返回類.
if(arguments.length == 0)
return clazz;
//如果無父類,此時constructor應(yīng)該為一個純對象,直接復(fù)制屬性返回.
if(!superclass){
extend(clazz.prototype, constructor);
return clazz;
}
var absObj = clazz.prototype,
sprPropty = superclass.prototype;
if(sprPropty){
//用于訪問父類方法
clazz.superclass = sprPropty;
extend(absObj, sprPropty);
//調(diào)用屬性構(gòu)造函數(shù)創(chuàng)建屬性,這個是實(shí)現(xiàn)關(guān)鍵.
extend(absObj, constructor(sprPropty));
// 子類實(shí)例直接通過obj.superclass訪問父類屬性,
// 如果不想造成過多引用,也可把這句注釋掉,因?yàn)槎鄶?shù)時候是沒必要的.
absObj.superclass = sprPropty;
//
clazz.constructor = constructor;
}
return clazz;
}
//
//創(chuàng)建一個動物類
//
var Animal = CC.create(null, {
//屬性
footprint : '- - - - - - =',
//類初始化方法,必須的,當(dāng)用 new 生成一個類時該方法自動被調(diào)用,參見上定義.
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 +' .');
}
});
//
//創(chuàng)建一個Duke類
//
var Duke = CC.create(Animal, function(superclass){
//在這可以定義一些類全局靜態(tài)數(shù)據(jù),該類每個實(shí)例都共享這些數(shù)據(jù).
//計算實(shí)例個類,包括派生類實(shí)例.
var static_instance_counter = 0;
function classUtilityFuncHere(){ }
//返回類具體屬性.
return {
//重寫初始化方法
//@override
initialize : function(options) {
alert('Initializing Duke class..');
//調(diào)用父類初始化,這種方法比一般其它庫的要簡潔點(diǎn)吧,可以不管父類是什么.
superclass.initialize.call(this, options);
//做一些子類喜歡做的事.
alert('Duke initialize method is called.');
//讀取或修改類靜態(tài)屬性
static_instance_counter++;
},
//重寫move方法,增加Duke自己的移動方式.
move : function(){
this.footprint = this.footprint + 'zzzzzzzz';
superclass.move.call(this);
},
//重寫eat方法,注意,里面不調(diào)用父類方法,即父類eat被覆蓋了.
eat : function(){
alert('Duke is eating..');
},
//新增一個say方法,顯示當(dāng)前已經(jīng)初始化的Duke類實(shí)例數(shù)量.
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();
})();
js并不是一種面向?qū)ο虻恼Z言, 沒有提供對類的支持, 因此我們不能像在傳統(tǒng)的語言里那樣 用class來定義類, 但我們可以利用js的閉包封裝機(jī)制來實(shí)現(xiàn)js類, 我們來封裝一個簡的Shape類.
復(fù)制代碼 代碼如下:
function ShapeBase() {
this.show = function()
{
alert("ShapeBase show");
};
this.init = function(){
alert("ShapeBase init");
};
}
這個類里定義了兩個方法:show和init, 需要注意的是這里用到了this來聲明, 而不是var, 因?yàn)橛胿ar是用來定義私有方法的.
另外, 我們還可以用prototype屬性來定義Shape的方法.
復(fù)制代碼 代碼如下:
ShapeBase.prototype.show=function()
{
alert("ShapeBase show");
}
ShapeBase.prototype.init=function()
{
alert("ShapeBase init");
}
上面這種寫法看起來不太直觀,我們可以將所有的方法寫在一起.
復(fù)制代碼 代碼如下:
ShapeBase.prototype={
show:function()
{
alert("ShapeBase show");
},
init:function() {
alert("ShapeBase init");
}
};
現(xiàn)在, 類是寫好了, 讓我們寫個js來測試下, 看看結(jié)果是不是跟我們想象的一樣呢?
復(fù)制代碼 代碼如下:
function test(src){
var s=new ShapeBase();
s.init();
s.show();
}
看到了吧, 其調(diào)用方式和C#一模一樣, 而結(jié)果也如我們所料.
到目前為止, 我們學(xué)會了如何創(chuàng)建js的類了, 但還只是實(shí)例方法,要是實(shí)現(xiàn)跟C#中的靜態(tài)方法要怎么做呢?
其實(shí), 實(shí)現(xiàn)js的靜態(tài)方法很簡單, 看下面如何實(shí)現(xiàn):
復(fù)制代碼 代碼如下:
//靜態(tài)方法
ShapeBase.StaticDraw = function()
{
alert("method draw is static");
}
2. 實(shí)現(xiàn)JS類抽象和繼承
同樣, js中也不支持類繼承機(jī)制,但我們可以通過將父類prototype中的成員方法復(fù)制到子類的prototype中來實(shí)現(xiàn).
和類的繼承一樣,JavaScript也沒有任何機(jī)制用于支持抽象類.但利用JavaScript語言本身的性質(zhì).可以實(shí)現(xiàn)自己的抽象類.
首先來看看js中的虛方法, 在傳統(tǒng)語言中虛方法是要先定義的, 而包含虛方法的類就是抽象類,不能被實(shí)例化,而在JavaScript中,虛方法就可以看作該類中沒有定義的方法,但已經(jīng)通過this指針使用了.
和傳統(tǒng)面向?qū)ο蟛煌氖牵@里虛方法不需經(jīng)過聲明,而直接使用了, 并且類也可以被實(shí)例化.
先定義object的extend方法, 一個為靜態(tài)方法,一個為實(shí)例方法, 這兩個方法用于實(shí)現(xiàn)繼承的prototype復(fù)制
復(fù)制代碼 代碼如下:
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]);
}
接下來我們實(shí)現(xiàn)一個繼承類Rect, 這里先用一種簡單的方法來實(shí)現(xiàn)。
復(fù)制代碼 代碼如下:
function Rect() {
}
Rect.prototype = ShapeBase.prototype; //只這一句就行了
//擴(kuò)充新的方法
Rect.prototype.add=function() {
alert("Rect add");
}
這種方法不能用于重寫,如果改變了show方法, ShapeBase的show也會指向同一函數(shù)可能是由于prototype賦值只是簡單的改變指向地址.
如果上面也定義了:
Rect.prototype.show=function() {
alert("Rect show");
}
那么執(zhí)行結(jié)果如下:
function test(){
var s=new ShapeBase();
s.show(); //結(jié)果:Rect show
var r=new Rect();
r.show(); //結(jié)果:Rect show
r.add();
}
我們再使用object.extend實(shí)現(xiàn)繼承, 并實(shí)現(xiàn)一個oninit虛方法, 修改ShapeBase如下:
復(fù)制代碼 代碼如下:
ShapeBase.prototype={
show:function()
{
alert("ShapeBase show");
},
initialize:function () {
this.oninit();
}
};
實(shí)現(xiàn)Rect類繼承.
復(fù)制代碼 代碼如下:
Rect.prototype=(new ShapeBase).extend({
//添加新的方法
add:function() {
alert("Rect add");
},
//使用這種方法可以重寫show方法
show:function() {
alert("Rect show");
},
//實(shí)現(xiàn)虛方法
oninit:function() {
alert("Rect oninit");
}
})
現(xiàn)在我們的類寫好了, 測試下看看:
復(fù)制代碼 代碼如下:
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")
}
另外,在網(wǎng)上看到一篇用專門的對象來創(chuàng)建類,代碼如下:
復(fù)制代碼 代碼如下:
//
//對象屬性復(fù)制方法,很多庫都有實(shí)現(xiàn),如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 用于創(chuàng)建類
//
CC.create = function(superclass, constructor){
var clazz = (function() {
this.initialize.apply(this, arguments);
});
//如果無參數(shù),直接返回類.
if(arguments.length == 0)
return clazz;
//如果無父類,此時constructor應(yīng)該為一個純對象,直接復(fù)制屬性返回.
if(!superclass){
extend(clazz.prototype, constructor);
return clazz;
}
var absObj = clazz.prototype,
sprPropty = superclass.prototype;
if(sprPropty){
//用于訪問父類方法
clazz.superclass = sprPropty;
extend(absObj, sprPropty);
//調(diào)用屬性構(gòu)造函數(shù)創(chuàng)建屬性,這個是實(shí)現(xiàn)關(guān)鍵.
extend(absObj, constructor(sprPropty));
// 子類實(shí)例直接通過obj.superclass訪問父類屬性,
// 如果不想造成過多引用,也可把這句注釋掉,因?yàn)槎鄶?shù)時候是沒必要的.
absObj.superclass = sprPropty;
//
clazz.constructor = constructor;
}
return clazz;
}
//
//創(chuàng)建一個動物類
//
var Animal = CC.create(null, {
//屬性
footprint : '- - - - - - =',
//類初始化方法,必須的,當(dāng)用 new 生成一個類時該方法自動被調(diào)用,參見上定義.
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 +' .');
}
});
//
//創(chuàng)建一個Duke類
//
var Duke = CC.create(Animal, function(superclass){
//在這可以定義一些類全局靜態(tài)數(shù)據(jù),該類每個實(shí)例都共享這些數(shù)據(jù).
//計算實(shí)例個類,包括派生類實(shí)例.
var static_instance_counter = 0;
function classUtilityFuncHere(){ }
//返回類具體屬性.
return {
//重寫初始化方法
//@override
initialize : function(options) {
alert('Initializing Duke class..');
//調(diào)用父類初始化,這種方法比一般其它庫的要簡潔點(diǎn)吧,可以不管父類是什么.
superclass.initialize.call(this, options);
//做一些子類喜歡做的事.
alert('Duke initialize method is called.');
//讀取或修改類靜態(tài)屬性
static_instance_counter++;
},
//重寫move方法,增加Duke自己的移動方式.
move : function(){
this.footprint = this.footprint + 'zzzzzzzz';
superclass.move.call(this);
},
//重寫eat方法,注意,里面不調(diào)用父類方法,即父類eat被覆蓋了.
eat : function(){
alert('Duke is eating..');
},
//新增一個say方法,顯示當(dāng)前已經(jīng)初始化的Duke類實(shí)例數(shù)量.
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();
})();
相關(guān)文章
javascript中類的定義及其方式(《javascript高級程序設(shè)計》學(xué)習(xí)筆記)
javascript也是一種面向?qū)ο蟮木幊陶Z言。但是javascript中的類相關(guān)的東西(類的定義,原型鏈,繼承等)卻不是很好理解,特別是繼承。2011-07-07Javascript 面向?qū)ο螅ㄒ唬?共有方法,私有方法,特權(quán)方法)
最近在網(wǎng)上盾一些JS面向?qū)ο蟮臇|西。把其他高手們總結(jié)的東西,加上自己的理解,總結(jié)一下2012-05-05JavaScript 使用簡略語法創(chuàng)建對象的代碼
JavaScript 使用簡略語法創(chuàng)建對象的代碼 ,需要的朋友可以參考下。2010-01-01js面向?qū)ο?多種創(chuàng)建對象方法小結(jié)
js面向?qū)ο?多種創(chuàng)建對象方法小結(jié),需要的朋友可以參考下2012-05-05Javascript面向?qū)ο髷U(kuò)展庫代碼分享
最近一直在用js做項目,遇到了許多需要應(yīng)用面向?qū)ο髞碓O(shè)計的功能,由于js對OOP的原生支持還不是很完善,所以就寫了一個面向?qū)ο蟮臄U(kuò)展庫用做底層支持,現(xiàn)在把它單獨(dú)整理出來,完善了一些功能,在這里分享一下2012-03-03