asfman
android developer
posts - 90,  comments - 213,  trackbacks - 0

在JavaScript面向对象编程中使用继承(5)

    明天就要回老家去过年了,关于这个"在JavaScript面向对象编程中使用继承"的话题居然还没有说完。如果不完成,留下来一拖就拖到明年去了。所以还是抽空把它写完,今年的事情今年做,明年还有更重要的任务呢!~~ 下面继续来看茴香豆的"茴"字第四种写法。

    这"茴"字的第四种写法,附加继承法,虽然是我自己杜撰出来的,而且还有一些前面三种继承法的影子,不过这个方法不可否认的,可以把前面说到继承的问题都cut掉。下面我们就来仔细说说到底它是为什么这么有武功和智慧的呢?

    附加继承法的原理:

    附加继承法的关键代码是其构找函数ArrayList04()中的:
  this.base = new CollectionBase();
  
  
for ( var key in this.base )
  {
      
if ( !this[key] )
      {
          
this[key] = this.base[key];
      } 
  }

    这里其实给不给this附加一个base并不重要,也一点不会影响我们的这个继承方法。首先我们看到在构造函数的第一句话中,我们立马就new了一个基类实例出来,这就说明我们的继承对基类的书写是没有任何要求的,用前面实例继承法中的说法就是,只要脚本引擎认为正确的类就都可以。我们知道构造继承法为什么有问题呢?就是因为它始终没有创建基类的实例。而原型继承法虽然也创建了基类实例,不过它把积累实例直接赋给了子类的prototype属性,以至于搞的对子类书写有特殊的要求。

    然后接下来一个for( in )循环,把基类具有的所有属性和方法都附加到子类的实例this中了,这也是我把这个继承方法叫附加法的原因。这一步和构造继承法的原理相当的类似,只是构造继承法是用了this作用域置换的一个技巧,把这个附加的过程让基类构造函数来完成了,不过同时也给构造继承法带来基类书写的特别要求,不能使用其prototype特性。当然附加法仍然是没有这个要求的。

    附加继承法的Update:
 Object.prototype.Extends = function(BaseClass)
 {
     
if ( arguments.length >= 6 )
     {
         
throw new Error('Only can supprot at most 5 parameters.');
     }
    
var base;
    
if ( arguments.length > 1 )
     {
         
var arg01 = arguments[1];
         
var arg02 = arguments[2];
         
var arg03 = arguments[3];
        
var arg04 = arguments[4];
         base 
= new BaseClass(arg01, arg02, arg03, arg04);
     }
    
else
     {
         base 
= new BaseClass();
     }
     
for ( var key in base )
     {
         
if ( !this[key] )
         {
             
this[key] = base[key];
             
if ( typeof(base[key]) != 'function' )
             {
                 
delete base[key];
             }
         }
     }
     
this.base = base;
     
// base.Inherit = this;
 
};
    
    这样我们就的继承就可以直接写成:
 function ArrayList04()
 {
     
this.Extends(CollectionBase);
     
// ...
 
}

    同时还提供了对基类继承时,传递参数给基类的支持,比如:
 function ListItem()
 {
     
this.Extends(ListItemBase, text, value);
     
// ...
 
}

    对于基类,会执行new ListItemBase(text, value);这样的操作来生成基类的实例。

    附加继承法的缺陷:

    从目前我的使用来看,如果不使用override技术来重写方法,然后还在新的方法中去调用基类的方法(这个技术我会以后再讲,因为它不影响也不属于我们今天讨论的继承方式的这个话题)的话。附加法基本没有缺陷,一定要说有就的话就是:使用一个for( in )循环来进行基类的导入,语法上很ugly:(

    附加继承法的示例:
 document.write('附加继承法:<br>'); 
 
var arrayList41 = new ArrayList04();
 arrayList41.Add('a');
 arrayList41.Add('b');
 arrayList41.foo();
 
var arrayList42 = new ArrayList04();
 arrayList42.Add('a');
 arrayList42.Add('b');
 arrayList42.Add('c');
 arrayList42.foo();

    示例运行结果为:
 附加继承法:
 [class ArrayList04]: 
2: a,b
 [class ArrayList04]: 
3: a,b,c

    小结:附加继承法是看起来最不像继承,但却是实际使用中最sexy(PS:这是我们boss对好代码的称呼)的解决方案。其override也非常的清晰明了,只要在this.Extends(BaseClass);语句后有同名的方法被导入子类,就会自动覆盖从基类中导入的方法,实现override的效果。

    使用场景:anywhere, anytime, anybody...

    这话似乎说大了,完美的东西一定是没有的,附加继承法也是有缺陷的,只不过这个缺陷不属于继承这个范畴,而是对其它OO编程特性的模拟中出现的问题,以后再谈。再唐僧一下:光是类的继承和使用,附加继承法是没有任何问题的。

    总算完成了JScript模拟面向对象编程中实现继承的各种研究。

    The End.

posted on 2005-02-07 00:05 birdshome 阅读(3894) 评论(28)  编辑 收藏 收藏至365Key 所属分类: JScript&DHTML开发

评论

# re: 在JavaScript面向对象编程中使用继承(5)  回复   

还没走哪?送你的新年礼物!呵呵
2005-02-07 00:23 | idior

# re: 在JavaScript面向对象编程中使用继承(5)  回复   

等mm一起,明天(哦,已经今天了)中午走:)
非常感谢你的礼物,新年快乐:)
2005-02-07 00:26 | birdshome

# re: 在JavaScript面向对象编程中使用继承(5)  回复   

1、对子类的书写仍有特殊要求
2、在子类的constructor体后以子类名.prototype.***=()形式定义的属性或方法无法传递给子类的实例对象!
3、仍会存在“浅拷贝”问题。
2005-02-16 17:20 | cmbscqhd

# re: 在JavaScript面向对象编程中使用继承(5)  回复   

哦?当new 子类名()的时候,子类中使用prototype导入的属性和方法都会被保留,怎么回有书写要求?"浅拷贝"问题又从何谈起呢?
2005-02-16 17:34 | birdshome

# 在JavaScript面向对象编程中使用继承(5) [TrackBack]  回复   

Ping Back来自:blog.csdn.net
雪候鸟引用了该文章,地址:http://blog.csdn.net/xuehouniao/archive/2005/02/17/290283.aspx
2005-02-17 00:19 | 雪候鸟

# re: 在JavaScript面向对象编程中使用继承(5)  回复   

对于原型继承的缺点,我也做了一些改进,一部分思路和你的一样,但我没有仔细测试你的代码,妄加评论,sorry!
建议:将Object.prototype.Extends = function(BaseClass)
改为Function.prototype.Extends = function(BaseClass),因为类的实例对象的类型也是object,但它是不能再继承的,只能被拷贝了。
2005-02-18 11:26 | cmbscqhd

# re: 在JavaScript面向对象编程中使用继承(5)  回复   

已修正,我当时确实没有太推敲这里,只是觉得所有对象都来之Object,就把Extends附加到Object的原型里了:} 真是贻笑大方哦
2005-02-18 11:39 | birdshome

# re: 在JavaScript面向对象编程中使用继承(5)  回复   

@cmbscqhd
俺又被你误导了Q_Q,我这里用Object的原型导入Extends只是为了简化我调用。如果要使用Function的原型来到入Extends,和我的意图完全不一样的。白改了半天代码,555...
2005-02-18 16:20 | birdshome

# re: 在JavaScript面向对象编程中使用继承(5)  回复   

也就是说我这个附加继承法,就是要把Extends导入Object的原型中去,否这调用Extends的方式和方法内部的实现也要改动。
2005-02-18 16:21 | birdshome

# re: 在JavaScript面向对象编程中使用继承(5)  回复   

this.base = new CollectionBase();

for ( var key in this.base )
{
if ( !this[key] )
{
this[key] = this.base[key];
}
}

It's sexy! 学习!

2、在子类的constructor体后以子类名.prototype.***=()形式定义的属性或方法无法传递给子类的实例对象!
3、仍会存在“浅拷贝”问题。
改为Function.prototype.Extends = function(BaseClass),因为类的实例对象的类型也是object,但它是不能再继承的,只能被拷贝了。
没看懂 cmbscqhd 的意思。



鸟食轩 的进步速度太快,基本上是我的四倍左右,强烈要求 鸟食轩 说出自己的学习方法和学习资料!
2005-02-19 06:27 | 辣妹子

# re: 在JavaScript面向对象编程中使用继承(5)  回复   

@辣妹子
我想起来cmbscqhd的意思了,它可能是说比如这样:
<script language="javascript">
function Base()
{
    
this.Name = 'base';
}

function base.prototype.ShowName()
{
    alert(
this.Name);
};

function Child()
{
    
this.Extends(Base);
    
this.Description = 'I'm a Child class.';
}

function Child.prototype.ShowName()
{
    alert(
this.Name + ', ' + this.Description);
};
</script>
    上面这段代码没有任何的特别,完全就是使用附加继承法的一个示例。
 
    可是如果继续用JScript的类编写概念来看,问题就出来了,这时如果我们在这个代码块后面(其实就是this.Extends这句)再写Base.prototype.xxxMethod = function(){...};这个时候就不能在被Child使用了。同时cmbscqhd说的浅拷贝,也应该是指的这个吧?
2005-02-19 19:01 | birdshome

# re: 在JavaScript面向对象编程中使用继承(5)  回复   

如果他的 浅拷贝 的意思是这样,真有点用词不妥!^_^
2005-02-19 22:44 | 辣妹子

# re: 在JavaScript面向对象编程中使用继承(5)  回复   

@辣妹子
其实对于在this.Extends(...)后执行的的Base.prototype.xxxMethod = function(){...};为基类导入的原型方法还是可以在Child的实例中调用的,只是需要限制基类的写法,会显得比较的混乱了。
而已我倾向的意思是,既然都使用OO的观点了,就该把我们模拟OO编程中的某些JS特性使用OO规范来约束,像在this.Extends(...)后再为基类添加原型方法,是完全不合和OO的规范的东西嘛,一个字,乱。
2005-02-19 22:59 | birdshome

# re: 在JavaScript面向对象编程中使用继承(5)  回复   

由于我的妄言,给大家造成理解上的混乱,在此向各位至谦!
经过仔细测试版主的Extends代码,我的理解是: this.Extends(CollectionBase);此处的this是指向子类的实例的;子类的实例当然是Object类型!!
也就是说,你是利用js中this指针的特殊性来完成继承机制的,实质上这是不符合OO规范的->继承函数只能由子类调用,怎么能由子类的实例变量调用呢?

即使如此,仍会存在继承后变量的共享引用问题,如:
var baseClass=function()
{
this.name=new Array('oop','ww');
this.add=function(item){this.name[this.name.length]=item;}
}
function son(){
this.Extends(baseClass);
}
function ss(){this.ss_name=["ss"];this.ss_add=function(item){this.ss_name[this.ss_name.length]=item;return this.ss_name}}

son.prototype.ss=new ss

var son0=new son;
var son1=new son;

son1.ss.ss_add("son1")
son0.ss.ss_add("son0")

alert(son1.ss.ss_name);
alert(son0.ss.ss_name);

结果都是:ss,son1,son0

我是一个新手,不对的地方请多包涵
2005-02-21 18:13 | cmbscqhd

# re: 在JavaScript面向对象编程中使用继承(5)  回复   

@cmbscqhd
讨论问题嘛,越独特的观察点,与能给出有意义的建议和启发的:)
 
第一点,关于this.Extends(CollectionBase);这个调用语法,我已经解释过了。我就是利用的JS的this的作用域变换技巧来设计的,因为开始我的Extends函数的内容是写在子类中的。按你说建议,把Extends导入到Function的prototype中,这样一来看起来确实不错,可是并不是我本身的意图。同时我也试着该了一下,发现修改后不但要使用ClassName.Extends的方式去调用,而且还要修改整个Extends的实现,所以我偷懒放弃了
 
第二点,关于OO规范的问题,由于JScript只是object-based的语言,我们只是把它模拟来可以像OOP那样去编写程序,但它的本质不是传统OO,有些东西是怎么也模拟不出来的(即使就算都弄出来,但是调用复杂也是没有意义了,是把?)。所以我们模拟OO,但又不拘泥于OO,取我们所需要的,又能很好的实现和调用,不就很perfect了么?
 
第三点,关于你的示例,看看我刚写的'JScript中的prototype(原型)属性研究'可能你就明白了:)
2005-02-21 18:33 | birdshome

# 关于继承的另一种实现  回复   

Function.prototype.childClass=function()
{ var base,i,xx;
base=new this;

function _childClass()
{
for(i in base)
{
if ( !this[i] )this[i] = base[i];
}
for(i in _childClass.prototype)
{
xx= _childClass.prototype[i];
this[i]=xx;
}

}
return _childClass;
}

var baseClass=function()
{
this.name=['oop','ww'];
this.length=2;
this.add=function(item){this.name[this.name.length]=item;this.length++}
this.getName=function(){return this.name}
}
var son=baseClass.childClass();
var son0=new son;
var son1=new son;
alert(son1.name);
son1.add("zjx")
alert(son1.name);
alert(son0.name);

这个示例已经在子类内部构造了this.××=××,可为什么不能解决共享引用的问题呢? (为简化便,省略了引入基类实参的代码)
2005-02-21 22:04 | cmbscqhd

# re: 在JavaScript面向对象编程中使用继承(5)  回复   

<html>
<head>
    
<title>JScript Inherit Research</title>
    
<meta name="author" content="cmbscqhd;birdshome@博客园" />
</head>
<body>
    
<script language="javascript">
    Object.prototype.clone 
= function()
    
{
         
var objClone = new this.constructor();
         
for ( var key in this )
         
{
             
if ( objClone[key] != this[key] )
             

                  objClone[key] 
= this[key];
             }

         }

         
return objClone; 
    }
    
    
</script>
    
<script language="javascript">
    Function.prototype.childClass 
= function() 
    
{
         
var base, i, xx; 
         base 
= new this

         
function _childClass() 
         
{
             
for( i in base) 
             

                  
if ( !this[i] )
                  
{
                       
if ( typeof(base[i]) == 'object')
                       
{
                            
this[i] = base[i].clone();
                       }

                       
else
                       
{
                            
this[i] = base[i];
                       }
 
                  }
 
             }
 
             
for( i in _childClass.prototype) 
             

                  xx 
= _childClass.prototype[i];
                  
this[i] = xx; 
             }
 
         }
 
         
return _childClass; 
    }
 

    
var baseClass = function() 
    

         
this.name = ['oop','ww']; 
         
this.length = 2
         
this.add = function(item)
         
{
              
this.name[this.name.length] = item;
              
this.length++;
         }
 
         
this.getName = function()
         
{
              
return this.name;
         }
 
    }
 
    
var son = baseClass.childClass(); 
    
var son0 = new son; 
    
var son1 = new son; 
    alert(son1.name); 
    son1.add(
"zjx");
    alert(son1.name); 
    alert(son0.name); 
    
</script>
</body>
</html>
    你的问题出在语句:this[i] = base[i],这里有发生shallow copy的错误的可能,而且对于Array实例,确实就发生了。
2005-02-21 22:41 | birdshome

# re: 在JavaScript面向对象编程中使用继承(5)  回复   

虽然你这个也是一种继承方法,不过子类的写法是不是太bt了。just a joke:)
2005-02-21 22:47 | birdshome

# re: 关于继承的另一种实现  回复   

这个继承方法可以获得对子类prototype子集的引用,也许可以完全解决“shallow copy ”问题。并且在son=parent.childClass()后可以再定义son的init()方法继续定义子类,所以我认为这个方法值得考虑。
2005-02-23 09:13 | cmbscqhd

# re: 在JavaScript面向对象编程中使用继承(5)  回复   

不好意思,没有看明白“并且在son=parent.childClass()后可以再定义son的init()方法继续定义子类”,是什么意思,可以示例一下吗?
2005-02-23 10:38 | birdshome

# re: 在JavaScript面向对象编程中使用继承(5)  回复   

var son=parent.childClass();
son.init=function()
{
var self=this.prototype;
self.name="oop";
}
son.init();
son0=new son;
.
.
2005-02-23 11:08 | cmbscqhd

# re: 在JavaScript面向对象编程中使用继承(5)  回复   

你的这个方法,不得不说理解起来有些复杂。而且也不能解决son0=new son();后在baseClass.prototype.xxxMethod = ...的问题。
2005-02-23 11:20 | birdshome

# re: 在JavaScript面向对象编程中使用继承(5)  回复   

Object.prototype.Extends = function(BaseClass)
你这么实现的话,"Extends"方法在实例化类的时候才会执行,即继承关系在运行时建立的。但是我感觉,继承关系应该在类的定义阶段就应该建立。为什么不这样写呢?:
Function.prototype.Extends = function(BaseClass)

function f(){
//......
}

f.Extends(baseCls);

这样的话,我自己感觉会舒服点。
2005-03-18 17:57 | 麻袋

# re: 在JavaScript面向对象编程中使用继承(5)  回复   

@麻袋
本来js使用OO就是模拟为主,你要喜欢f.Extends,就把Extends导入Function的prototype中,这个只是个人喜好,效果完全一样:)
我比较懒,把Extends导入Object中,每次继承基类,写法都是一样的:this.Extends(baseClass),连类名都省了。
2005-03-18 18:06 | birdshome

# re: 在JavaScript面向对象编程中使用继承(5)  回复   

但是在判断对象的类型时可能会有点问题(也不能算是问题)。现在刚刚想到这一点,还没有动手。

我如果想实现类似“实现接口”这样的功能时,判断一个对象是不是某一个已实现指定接口的类的实例,可能会麻烦点。
2005-03-18 18:39 | 麻袋

# re: 在JavaScript面向对象编程中使用继承(5)  回复   

@麻袋
嗯,实现了欢迎share出来哦~

不过我对JavaScript模拟OOP的观点是:这只是为了方便JS编程的一种设计,如果要照搬经典OO,会有引入很多蹩脚和不必要的东西,感觉可能不太划算。
2005-03-18 18:44 | birdshome

# re: 在JavaScript面向对象编程中使用继承(5)  回复   

实在是搞不懂
for( i in _childClass.prototype)
{
xx = _childClass.prototype[i];
this[i] = xx;
}
}
return _childClass;

是做什么
为什么不省略掉直接return this呢。
好奇怪。而且这段代码做什么用的也想不通。跟踪了也看不懂。唉。实在是愚钝啊。
2005-07-08 10:07 | 孙玉婷

# re: 在JavaScript面向对象编程中使用继承(5)  回复   

     你的理解确实不对,你看到的那个 return _childClass; 不是用来返回它前面 this的,你看下面的语句:
var son = baseClass.childClass();  
var son0 = new son;  
var son1 = new son; 

     当执行第一句的时候,更本没有执行Function.prototype.childClass()里面的_childClass,直接就放回了_childClass的构造函数;第二句话var son0 = new son;,由于上面返回了_childClass,son其实就是_childClass,只不过这里_childClass是一个内部函数,它的变量作用域比普通函数多一级,就是Function.prototype.childClass(),也就是说我们new son;的时候,可以访问到Function.prototype.childClass里面的base这个变量。所以我们执行new son;后根本不需要return语句来返回任何的东西。
     你的跟踪是不是有问题?用step into很容易就能看出来的。
posted on 2006-03-13 16:34 汪杰 阅读(217) 评论(0)  编辑 收藏 引用 所属分类: javascript
只有注册用户登录后才能发表评论。

<2024年4月>
31123456
78910111213
14151617181920
21222324252627
2829301234
567891011

常用链接

留言簿(15)

随笔分类(1)

随笔档案(90)

文章分类(727)

文章档案(712)

相册

收藏夹

http://blog.csdn.net/prodigynonsense

友情链接

最新随笔

搜索

  •  

积分与排名

  • 积分 - 457612
  • 排名 - 6

最新随笔

最新评论

阅读排行榜

评论排行榜