(精)js继承探讨

(精)js继承探讨

每当我们说到 js 的继承时,在您的脑袋的第一反应就是 prototype原型机制来实现。但是您是否使用过其他的方法来实现继承呢,或者您是否了解其他实现方式及各种不同的继承实现机制的优缺点呢?

好了,下面我们就来看看几种比较常见的继承实现吧。

1、 prototype方式

var  BaseClass  = function ()
   
     {
   
       this .name  =   " 3zfp " ;
   
      this .age  =   100 ;
  
        this .ToString  =   function ()  {
 
         return   this .name + "   "   + this .age;
  
    } 
 
 } 
  
  var  Derived  =   function ()
  
    { 
  
      this .address  =   " ShenZhen " ;
 
 } 
 
 Derived.prototype  =   new  BaseClass();
 
 var  instance  =   new  Derived();
 
 instance.ToString();


这种方式最为简单,只需要让一个类的prototype为被继承的一个实例就ok,然后直接使用BaseClass的方法。

prototype 属性是啥意思呢? prototype即为原型,每一个对象( 由 function 定义出来)都有一个默认的原型属性,该属性是个对象类型。 并且该默认属性用来实现链的向上攀查。意思就是说,如果某个对象的属性不存在,那个将通过prototype属性对应的对象的来查找该对象的属性。 如果 prototype查找不到呢?js 会自动地找 prototype 的 prototype属性对应的对象来查找,这样就通过 prototype一直往上索引攀查,直到查找到了 该属性或者 prototype 最后为空("undefined");

例如:上例中的 instance.ToString()方法。js 会先在 instance 实例中查找是否有ToString()方法,因为没有,所以查找 Derived.prototype 属性, 而prototype  为 NewClass的一个实例,该实例有ToString() 方法,于是调用成功;同样给 instance 的name  属性赋值时也是查找 prototype来实现的。

注意,每一个对象得 prototype都默认对应一个object 对象,但是该对象不等于 Object;如何验证呢?看如下代码:

        var  foo  =   function ()  {} ;
 
        var  result  =  (foo.prototype == Object); 


这段代码的result 所得值为 false;

以下几个需要注意:

      typeof (Object.prototype)  ==   " object " ;
   
  
  
        typeof (Object.prototype.prototype)  ==   " undefined " ;
  

 
       var  obj  =   new  Object();
 
        typeof (obj.prototype)  ==   " undefined " ;

      
 
         var  obj  =    {} ;
        typeof (obj.prototype)  ==   " undefined " ;


2 、 apply  方式

var  BaseClass  = function ()
 
     {
  
     this .name  =   " 3zfp " ;
  
     this .age  =   100 ;
 
       this .ToString  =   function ()  {
  
         return   this .name + "   "   + this .age;
  
     } 
 
 } 
 
  var  Derived  =   function ()
 
    { 
  
        BaseClass.apply( this , new  Array());

     this .address  =   " ShenZhen " ;
 
 } 
 
  var  instance  =   new  Derived();
  
 instance.ToString();


在这种方式下,我们最需要理解的就是 apply 函数的作用。

该方法普遍的解释为用 A 方法去替换 B  方法。第一个参数为 B 方法的对象本身,第二个参数为一个数组,该数组内的值集合为需要传递给  A 方法对应的 参数列表,如果参数为空,即没有参数传递,则通过 new Array()来传递,null 无效。

一般的方式为:

但是在本例当中, apply 方法执行了两步操作。

第一:将BaseClass以apply传递的Array数组作为初始化参数进行实例化。

第二:将新生成的实例对象的所有属性( name,age , ToString 方法)复制到instance实例对象。 这样就实现了继承。

 var  foo  =   function ()
   
    {
 
        this .fooA  =   function ()  {
  
                this .fooB.apply( this , new  Array( " sorry " ));
 
        } 
 
         this .fooB  = function (str)
  
          {
 
               alert(str);
  
        } 
 
 } 
  
  new  foo().fooA();


3、call+prototype 方式

var  BaseClass  = function (name,age)
  
    {

     this .name  =  name;
  
    this .age  =  age;
  
      this .ToString  =   function ()  {
 
         return   this .name + "   "   + this .age;
 
    } 
  
} 
 
 var  Derived  =   function ()
 
   { 
  
      BaseClass.call( this , " 3zfp " , 100 );
 
    this .address  =   " ShenZhen " ;
 
 } 
 
 Derived.prototype  =   new  BaseClass();
  
  var  instance  =   new  Derived();
  
 instance.ToString();


其实,call函数和apply方式有很类似的作用,都是用A方法去替换B方法,但是参数传递不一样,call方法的第一个参数为B方法的对象本身,和面的参数列不用Array对象包装,直接依次传递就可以。

为什么作用类似, call 方式的实现机制却要多一条  Derived.prototype = new BaseClass(); 语句呢?那是因为 call方法只实现了方法的替换而没有作对象属性的复制操作。

call 方法实际上是做了如下几个操作:

例:

var  foo  =   function ()
   
    {
   
         this .fooA  =   function ()  {
  
               this .fooB.call( this , " sorry " );
 
      } 
  
       this .fooB  = function (str)
 
          {
 
              alert(str);
 
       } 
 
 } 
 
 new  foo().fooA();

 


则 this.fooB.call(this,"sorry")执行了如下几个操作:  1

www.zeeklog.com  - (精)js继承探讨

this .temp  =   this  .fooB;
 2

www.zeeklog.com  - (精)js继承探讨


 3

www.zeeklog.com  - (精)js继承探讨

this .temp( " sorry "  );
 4

www.zeeklog.com  - (精)js继承探讨


 5

www.zeeklog.com  - (精)js继承探讨

delete  ( this .temp);
 6

www.zeeklog.com  - (精)js继承探讨

其实,google Map API 的继承就是使用这种方式。大家可以下载的参考参考(maps.google.com)。

4 、 prototype.js 中的实现方式

Object.extend  =   function (destination, source)   {
  
    for  (property  in  source)   {
  
     destination[property]  =  source[property];
  
  } 
 
        return  destination;
 
 } 
  
  var  BaseClass  = function (name,age)  {
  
        this .name  =  name;
 
       this .age  =  age;
  
         this .ToString  =   function ()  {
 
               return   this .name + "   "   + this .age;
 
        } 
  
 } 
 
 var  Derived  = function ()
 
    {     
  
       BaseClass.call( this , " foo " , 100 );
 
        this .address  =   " singapore " ;
  
         this .ToString  =   function ()  {
 
              var  string  =  Derived.prototype.ToString.call( this );
 
               return  string  + "   " +   this .address;

       } 
 
} 
  
 Object.extend(Derived.prototype, new  BaseClass());
  
  var  instance  =   new  Derived();
 
 document.write(instance.ToString()); 


该方式,实际上是显式的利用了 apply 的原理来实现继承。先 var temp = new BaseClass(),再将temp 的属性遍历复制到 Derived.prototype中。for (property in source) 表示遍历某个对象的所有属性。但是私有属性无法遍历。例:

 var  foo  =   function ()
 2  
 3    {
 4  
 5         var  innerString  =   "" ;
 6  
 7         this .name  =   " 3zfp " ;
 8  
 9         this .age  =   100 ;
10  
11         function  innerToString()
12  
13           {
14  
15                return  innerString;
16  
17        } 
18  
19 } 
20  
21  var  f  = new  foo();
22  
23  var  eles  =   "" ;
24  
25  for  (property  in  f)
26  
27    {
28  
29        eles += "   " + property;
30  
31 } 
32  
33 document.write(eles); 


输出为 "name age"而没有"innerString" 和 "innerToString()";具体原理,以后有机会可以解释(包括私有变量,私有函数,私有函数的变量可访问性等等)。上面总结了种种继承方式的实现。但是每种方法都有其优缺点。

第一种方式,如何实现如下需求,需要显示 "3zfp__100";

 var  BaseClass  = function (name,age)
 2  
 3    {
 4  
 5      this .name  =  name;
 6  
 7      this .age  =  age;
 8  
 9       this .ToString  =   function ()  {
10  
11          return   this .name + "   "   + this .age;
12  
13     } 
14  
15 } 
16  
17  var  Derived  =   function (name,age)
18  
19    { 
20  
21      this .address  =   " ShenZhen " ;
22  
23 } 
24  
25 Derived.prototype  =   new  BaseClass();
26  
27  var  instance  =   new  Derived( " 3zfp " , 100 );
28  
29 document.write(instance.ToString());
30  
31  


我们通过运行可以发现,实际上输出的是 "undefined__undefined"。也就是说name和age没有被赋值。

oh,my god! 天无绝人之路。第二和第三种方法可以实现,具体如下:

 var  BaseClass  = function (name,age)
 2  
 3    {
 4  
 5      this .name  =  name;
 6  
 7      this .age  =  age;
 8  
 9       this .ToString  =   function ()  {
10  
11          return   this .name + "   "   + this .age;
12  
13     } 
14  
15 } 
16  
17  var  Derived  =   function (name,age)
18  
19    { 
20  
21        BaseClass.apply( this , new  Array(name,age));
22  
23      this .address  =   " ShenZhen " ;
24  
25 } 
26  
27  var  instance  =   new  Derived( " 3zfp " , 100 );
28  
29 document.write(instance.ToString());
30  
31 ______________________________________________
32  
33  --------------------------------------------------------------------- 
34  
35  var  BaseClass  = function (name,age)
36  
37    {
38  
39      this .name  =  name;
40  
41      this .age  =  age;
42  
43       this .ToString  =   function ()  {
44  
45          return   this .name + "   "   + this .age;
46  
47     } 
48  
49 } 
50  
51  var  Derived  =   function (name,age)
52  
53    { 
54  
55        BaseClass.call( this ,name,age);
56  
57      this .address  =   " ShenZhen " ;
58  
59 } 
60  
61 Derived.prototype  =   new  BaseClass();
62  
63  var  instance  =   new  Derived( " 3zfp " , 100 );
64  
65  
66  
67 document.write(instance.ToString());
68  
69  
70  


但是用apply方法也还是有缺点的,为什么?在js中,我们有个非常重要的运算符就是"instanceof",该运算符用来比较某个对向是否为某种类型。对于继承,我们除了是属于 Derived类型,也应该是BaseClass类型,但是。apply方式返回值为false((instance instanceof BaseClass) == false).由于prototype.js使用了类似apply的方式,所以也会出现这个问题。

啊,终极方法就是 call+prototype方式了,还是google 牛 X 。您可以试一下是否正确((instance instanceof BaseClass) == true)。

最后,就是多重继承了,由于js中prototype只能对应一个对象,因此无法实现真正意义上的多重继承。有一个js库模拟了多重继承,但是该库也额外重写了 instanceOf方法,用 _instanceOf和_subclassOf函数来模拟判断。该库的名字叫modello.js,感兴趣的可以搜索下载。

Read more

Linux学习总结(30)——优秀程序员喜欢用Linux操作系统

Linux学习总结(30)——优秀程序员喜欢用Linux操作系统

大多数人推荐Linux,基本上都会说Linux让你更高效、更优秀。 然而工具只是工具。 然而工具只是工具。 然而工具只是工具。 优秀程序员和不优秀程序员的区别首先是态度上的区别。 他们有自己的理想,思考很多,不管是项目开始之前还是在项目进行中,项目完成之后也会进行总结。 他们对待问题比较严谨,思考比较全面,在动手写代码之前肯定经过了一定的思考,对可能引入的问题进行预估。 而不优秀的程序员对待问题比较随意,也就是态度上不是那么认真,代码写的也比较随意:他们不会意识到他们的代码已经污染了整个项目。 除了态度上的不同,在能力上也有很大的差别。 遇到比较难缠的问题,优秀的程序员总是能从原理出发,一针见血的看出问题的真正原因,进而解决问题,而不优秀的程序员总是停留在表面——认为Linux会把你变得优秀就是停留在表面,只有你自己才能把你变优秀。 那么Linux的好处到底在哪里呢? 1.开源 这个我想不用解释。 2.多用户、多任务、多线程 Linux系统同时可以支持多个用户,每个用户对自己的文件设备有特殊的权利,能够保证各用户之间互不干扰,就像手机开了助手一样,同时登陆多

By Ne0inhk
Dubbo学习总结(5)——Dubbo服务架构及服务治理过程演进

Dubbo学习总结(5)——Dubbo服务架构及服务治理过程演进

单一应用  当网站流量很小时,只需一个应用,将所有功能都部署在一起,以减少部署节点和成本。 此时,用于简化增删改查工作量的 数据访问框架(ORM) 是关键。 垂直应用架构 当访问量逐渐增大,单一应用增加机器带来的加速度越来越小,将应用拆成互不相干的几个应用,以提升效率。 此时,用于加速前端页面开发的 Web框架(MVC) 是关键。 分布式服务架构 当垂直应用越来越多,应用之间交互不可避免,将核心业务抽取出来,作为独立的服务,逐渐形成稳定的服务中心,使前端应用能更快速的响应多变的市场需求。 此时,用于提高业务复用及整合的 分布式服务框架(RPC) 是关键。 流动计算架构 当服务越来越多,容量的评估,小服务资源的浪费等问题逐渐显现,此时需增加一个调度中心,基于访问压力实时管理集群容量,提高集群利用率。 此时,用于提高机器利用率的 资源调度和治理中心(SOA) 是关键。 在大规模服务化之前,应用可能只是通过RMI或Hessian等工具,简单的暴露和引用远程服务,通过配置服务的URL地址进行调用,通过F5等硬件进行负载均衡。 (1)

By Ne0inhk
ECMAScript 6学习总结(1)——ECMAScript 6入门简介

ECMAScript 6学习总结(1)——ECMAScript 6入门简介

ECMAScript 6简介 ECMAScript 6.0(以下简称 ES6)是 JavaScript 语言的下一代标准,已经在2015年6月正式发布了。它的目标,是使得 JavaScript 语言可以用来编写复杂的大型应用程序,成为企业级开发语言。 ECMAScript 和 JavaScript 的关系 一个常见的问题是,ECMAScript 和 JavaScript 到底是什么关系? 要讲清楚这个问题,需要回顾历史。1996年11月,JavaScript 的创造者 Netscape 公司,决定将 JavaScript 提交给国际标准化组织ECMA,希望这种语言能够成为国际标准。次年,ECMA 发布262号标准文件(ECMA-262)的第一版,规定了浏览器脚本语言的标准,并将这种语言称为 ECMAScript,这个版本就是1.0版。 该标准从一开始就是针对 JavaScript 语言制定的,但是之所以不叫

By Ne0inhk
软件架构模式

软件架构模式

分层架构 (Layered Architecture) 分层架构是最常见的架构,也被称为n层架构。多年以来,许多企业和公司都在他们的项目中使用这种架构,它已经几乎成为事实标准,因此被大多数架构师、开发者和软件设计者所熟知。比如MVC。 分层架构的一个特性就是 关注分离(separation of concerns) 。在层中的组件只负责本层的逻辑。组件的划分很容易让它们实现自己的角色和职责,也比较容易地开发,测试管理和维护。 我们需要这样的冗余,即使业务层没有处理业务规则,也要通过业务层来调用数据层,这叫 分层隔离 。对于某些功能,如果我们从表现层直接访问数据层,那么数据层后续的任何变动都将影响到业务层和表现层。 注意分层的 开闭原则 。如果某层是关闭的,那么每个请求都要经过着一层。相反,如果该层是开放的,那么请求可以绕过这一层,直接到下一层。 分层隔离有利于降低整个应用程序的复杂度。某些功能并不需要经过每一层,这时我们需要根据开闭原则来简化实现。 污水池反模式(architecture sinkhole anti-pattern) 分层架构是 SOLID原则

By Ne0inhk