您的位置:首页 > Web前端 > JavaScript

【09类和模块】——3:javascript中java式的类继承

2016-03-27 18:51 591 查看
上一篇博文【09类和模块】——2类和构造函数

这里我们来说一下javascript中java式的类继承,如果你有过java或其他类似的强类型的面向对象编程语言开发的经历的话,你会发现javascript和java的类的不同之处在于——javascript中的函数都是以值得形式出现的,方法和字段之间并没有太大的区别。如果属性是一个函数,那么这个属性就定义一个方法,否则,它只是一个普通的属性或者叫“字段”;

但是我们还是可以模拟出java中的类的

如果你看前面写的博文,你会知道javascript中的类牵扯三种不同的对象

1、构造函数对象(任何添加到构造函数对象中的属性都是类字段或者类方法)

2、原型对象(原型对象的属性被类的说有实例所继承)

3、实例对象(类的所有实例对象都独立的,直接给这个实例定义的属性不会被所有实例对象所共享)

javascript中定义类的步骤可以缩减为三步

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

2、给构造函数的prototype对象定义实例实例

3、给构造函数定义类字段和类属性

接下来我们将这三个步骤封装进一个简单的defineClass()函数中

先做一个准备工作,定义一个extend函数,用于将一个对象的属性和值复制到另外一个对象中

function extend(o,p){
for(var prop in p){//遍历p中的所有属性
o[prop]=p[prop];//将属性和值复制到o中
}
return o;
}


接下俩定义一个简单的类

function defineClass(constructor,methods,statics){
if(methods) extend(constructor.prototype,methods);
if(statics) extend(constructor,statics);
return constructor
}
/*
constructor——用以设置实例的属性的函数
methods——实例的方法,赋值到原型中
statics——类属性,复制至构造函数中
*/


利用上面封装的这个函数,我们重新定义一下【09类和模块】——2类和构造函数 中的Range类

var SimpleRange=defindeClass(
function(f,t){this.f=f;this.t=t},
{
includes:function(x){return this.from <=x && x<=this.to;},
foreach:function(f){
for(var x=Math.ceil(this.from);x<this.to;x++) f(x)
}
},
{upto:function(t){return new SimpleRange(o,t)}}
)


上面的是封装了一个defineClass()函数来实现类似java中的类,下面我们在定义一个更长一点的类——表示复数的类Complex.js类

/*
Complex.js这个文件定义了Complex类,用来描述复数
复数是实数和虚数的和,并且叙述i是-1的平方根
*/
function Complex(real, imaginary) {
if (isNaN(real) || isNaN(imaginary)) //确保两个参数都是数字
throw new TypeError();
this.r = real;        //复数的实部
this.i = imaginary;   //复数的虚部
}


类的实例方法定义为原型对象的函数值属性,这里定义的方法可以被所有的实例继承,并为他们提供共享的行为

注意:在这些方法中必须使用this关键字来存取实例属性

//下面的方法中that参数也必须是一个该累的实例化对象
//当前复数对象加上另外一个复数,并返回一个新的计算值的复数对象
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 Math.sqrt(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 &&  //that必须有定义且不能为null
that.constructor === Complex && //that必须是Complex的实例
this.r === that.r && this.i === that.i;//必须包含相同的值
};


类字段(比如常量)和类方法直接定义为构造函数的属性

需要注意的是,类的方法通常不使用this关键字,它们只对参数进行操作

/*
这里定义一些对复数运算有帮助的类字段,它们的命名全都是大写,用于表名它们是常量
*/
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 = /^\{([^,]+),([^}]+)\}$/;


上面是定义一个类的全过程,如果还需要增加实例方法,可以在原型对象上继续添加,这里我们用到了构造函数,实例字段、实例方法、类字段、类方法,下面我们类运用一下这个类

var c = new Complex(2,3) //使用构造函数实例化对象
var d = new Complex(c.i,c.r)//同样是实例化对象,只不过用到了对象c的属性
c.add(d).toString()  //=>"{5,5}":使用了实例的toString()方法
//下面这个稍微复杂一点,使用到了类方法和类字段
Complex.parse(c.toString()).//将实例对象c转换为字符串
add(c.neg()).//加上这个对象本身的负数
equals(Complex.ZERO)//结果永远是“零”,所以永远返回true


尽管javascript可以模拟出java式的类成员,但是java中很多重要的特性是无法在javascript中模拟的

1、对于java实例方法来说,实例字段可以用做局部变量,而不需要使用this关键字来引用它们,但是javascript没办法模拟这个特性,但可以使用with语句来近似的实现

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


2、在java中可以使用final声明字段为常量,并且可以将字段和方法声明为private,用来表示他们是私有成员且在类的外面是不可见的,在javascript中并没有这些关键字,但是javascript中的私有属性也可以用闭包里的局部变量来模拟

期待你阅读下一篇博文【09类和模块】——4:类的扩充
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: