《JS权威指南学习总结--9.3 JS中JAVA式的类继承》

内容要点:

一.JS中的类

    1.JAVA或其他类似强类型 面向对象语言的 类成员的模样

       实例字段:它们是基于实例的属性或变量,用以保存独立对象的状态。

       实例方法: 它们是类的所有实例所共享的方法,由每个独立的实例调用

       类字段:这些属性或变量是属于类的,而不是属于类的某个实例的。

       类方法:这些方法是属于类的,而不是属于类的某个实例的

     2.JS中的类牵扯三种不同的对象,三种对象的属性的行为和下面三种类成员非常相似:

        构造函数对象:

             之前提到,构造函数(对象)为JS的类定义了名字。任何添加到这个构造函数对象中的属性都是类字符和类方法(如果属性值是函数的话就是类方法)。

        原型对象:

             原型对象的属性被类的所有实例所继承,如果原型对象的属性值是函数的话,这个函数就作为 类的实例的方法 来调用

        实例对象:

            类的每个实例都是一个独立的对象,直接给这个实例定义的属性是不会为所有实例对象所共享的。定义在实例上的非函数属性,实际上是实例的字段。

     3.JS中定义类的步骤可以缩减为一个分三步算法。

           第一步,先定义一个构造函数,并设置初始化新对象的实例属性。

           第二步,给构造函数的prototype对象定义实例的方法。

           第三步,给构造函数定义类字段和类属性。

        我们可以将这三个步骤封装进一个简单的defineClass()函数中(这里用到了例6-2中的extend()函数和例8-3中的改进版)

          //一个用以定义简单类的函数

           function defineClass(constructor,  //用以设置实例的属性的函数

                                         methods, //实例的方法,复制至原型中

                                         static)   //类属性,复制至构造函数中

                                        {

                                        if (methods) extend( constructor,prototype,methods );

                                        if (statics) extend( constructor,statics );

                                         return constructor;

                                         }

               //这是Range类的另一个实现

                    var SimpleRange = defineClass(function(f,t){ this.f=f;this.t=t; },

                                                                 {

                                                                     includes:function(x){ return this.f <=x && x<=this.t;}

                                                                     toString:function(){ return this.f + "..." +this.t; }

                                                                  },

                                                                  {

                                                                      upto:function(t){ return new SimpleRange(o,t); }});

二.例9-3 Complex.js : 表示复数的类

      /*Complex.js : 这个文件定义了Complex类,用来描述复数。回忆一下,复数是实数和虚数的和,并且虚数i是-1的平方根*/                         

      /*这个构造函数为它所创建的每个实例定义了实例字段r和i,这两个字段分别保存复数的实部和虚部,它们是对象的状态*/

       function Complex(real,imaginary){

         if(isNaN(real) || isNaN(imaginary)) throw new TypeError();   //确保两个实参都是数字,如果不都是数字则抛出错误

         this.r = real; //复数的实部

         this.i = imaginary; //复数的虚部

       }

       /*类的实例方法 定义为原型对象的函数值属性,这里定义的方法可以被所有实例继承,并为它们提供共享的行为。需要注意的是,JS类额实例方法必须使用关键字this来存取实例的字段。*/

       //当前复数对象加上另一个对象,并返回一个新的计算和值后的复数对象

       Complex.prototype.add = function(that){ return new Complex(this.r + that.r , this.i + that.i); };

        //当前复数乘以另一个复数,并返回一个新的计算乘积之后的复数对象

        Complex.prototype.mul = function(that){ return new Complex(this.r * that.r - this.i*that.i , this.r*that.i + this.i*that.r); };

        //计算复数的模,复数的模定义为原点(0 , 0)到复平面的距离

        Complex.prototype.mag = function(){ return new Complex(this.r * this.r + this.i * this.i); };

        //复数的求负运算

        Complex.prototype.neg = function(){ return new Complex(-this.r , -this.i); };

       //将复数对象转换为一个字符串

         Complex.prototype.toString = function(){ return "{" +this.r + "," +this.i + "}"; };

       //检测当前复数对象是否和另外一个复数值相等

         Complex.prototype.equals = function(that){ return that !=null &&          //必须有定义且不能是null

                                                                         that.constructor === Complex &&     //并且必须是Complex的实例,

                                                                         this.r === that.r && this.i === that.i; //并且必须包含相同的值

                                                                       };

         /*类字段(比如常量)和类方法 直接定义为构造函数的属性。需要注意的是,类的方法通常不使用关键字this,它们只对其参数进行操作*/

         //这里预定义了一些对复数运算有帮助的类字段,它们的命名全都是大写,用以表明它们是常量(在ES5中,还能设置这些类字段的属性为只读)

         Complex.ZERO = new Complex(0,0);

         Complex.ONE = new Complex(1,0);

         Complex.I = new Complex(0,1);

         //这个类方法将由实例对象的toString方法返回的字符串格式解析为一个Complex对象,或者抛出一个类型错误异常

         Complex.parse = function(s){

               try{     //假设解析成功

                  var m = Complex._format.exec(s); //利用正则表达式进行匹配

                   return new Complex(parseFloat(m[1]),parseFloat(m[2]));

               }catch(x){      //如果解析失败则抛出异常

                     throw new TypeError("Can't parse ' " + s + " ' as a complex number.");

                          }

               };

           //定义类的"私有字段",这个字段在Complex.parse()中用到了下划线前缀表明它是类内部使用的,而不属于类的公有API的部分

            Complex._format = /^\{(^,]+),([^}]+)\}$/;

     代码分析:

     从例9-3中所定义的Complex类可以看出,我们用到了构造函数、实例字段、实例方法、类字段和类方法,看一下这段实例代码:

       var c = new Complex(2,3); //使用构造函数创建新的对象

       var d = new Complex(c.i , c.r); //用到了c的实例属性

       c.add(d).toString(); //=>"{5,5}":使用了实例的方法

      //这个稍微复杂的表达式用到了类方法和类字段

         Complex.parse(c.toString()).         //将c转换为字符串

                              add(c.neg()).          //加上它的负数

                              equals(Complex.ZERO)  //结果应当永远是“零”

 

posted @ 2016-09-06 16:56  承载梦想-韩旭明  阅读(356)  评论(0编辑  收藏  举报