阿不

潜水

  博客园 :: 首页 :: 博问 :: 闪存 :: 新随笔 :: 联系 :: 订阅 订阅 :: 管理 ::

原文地址Classes in JScript – Part III: Class Hierarchy and Data Encapsulation

原文发表日期】Published Wednesday, October 31, 2007 8:10

在这篇博客中,我们将会来讨论如何在Jscript中实现类的继承与封装。

我仍然会使用之前第一部分第二部分文章中所使用的示例代码,并按需要来修改它们。

function Rectangle (ht, wt) {

                this.height = ht;

                this.width=wt;

}

Rectangle.prototype.area = function() {return this.height * this.width;}

var rect1 = new Rectangle(5,10);

var rect2 = new Rectangle(2,4);

var rect1Area = rect1.area(); // rect1.area() will return 50.

var rect2Area = rect2.area(); // rect2.area() will return 8.

在支持而向对象的语言中都会有两个不同而有联系的概念:类和实例。在第一篇文章中,我们定义的类是对象的框架结构,在类中定义了对象所拥有的字段属性,类别和对对象进行操作的方法。类是一个抽象的概念,一个类可以拥有很多不同的实例,每个实例都有自己不同的状态。

但是,基于prototype 的语言(比如:JScript),并没有对类和实例进行区分。这种语言中只有对象(objects)的概念。基于prototype的语言中,都提供了一个prototype对象,这个对象可以为新对象提供初始化属性的模板。任何对象都可以给prototype属性赋不同的值。

在支持Class的语言中,类的继承关可以通过类的来定义。定义类的时候,我们可以指定它继承于一个已经存在的类。

前面的文章中已经介绍过了,在Jscript中并不支持Class关键字(来定义类)。它是通过Constructor和Prototype对象来模拟类。因此,类的继承也可以通过Constructor和Prototype来实现。

让我们扩展上类的例子,定义一个带有坐标的Rectangle。理论上,我们可能需要定义8个点(topLeftX, topLeftY, topRight(X & Y),bottomLeft(X & Y) and bottomRight(X & Y) )才能完全定位坐标,但是我们这里将它简化一下,只需要前面两个。

定义PositionedRectangle:

function PositionedRectangle (topLeftX,topLeftY,height,width) {

                this.topLeftX = topLeftX;

                this.topLeftY = topLeftY;

}

修改PositionedRectangle的prototype值为Rectangle的一个实例。

PositionedRectangle.prototype = new Rectangle;

类似的,我们可以创建一个PositionedRectangle对象。

var prObj = new PositionedRectangle(2,3,4,5);

prObj创建的时候,它会从Rectangle类中继承height和width,因为我们修改了PositionedRectangle的值为Rectangle的一个实例。

通过下面的代码,我们可以看到topLeftX,topLeftY,height,width不同的值:

alert(prObj.topLeftX+" "+ prObj.topLeftY+" "+ prObj.height+" "+ prObj.width);

// 显示 2 3 undefined undefined

因为Rectangle的height 和 width,未被赋值,所以我们看到它们的值是undefined。这就是以上做法的不完善的地方。如果我们像下面这样在构造器中给height和width属性设置默认值:

function Rectangle () {

                this.height = 1;

                this.width=1;

}

这样的结果会是2 3 1 1。但是它不是我们希望的结果,因为参数值未被传递下来。

为了解决这个问题,我们可以往构造器中添加一些代码:

function PositionedRectangle (topLeftX,topLeftY,height,width) {

                this.topLeftX = topLeftX;

                this.topLeftY = topLeftY;

//其中一种做法分别新建这两个属性

this.height = height;

this.width=width;

//另一种做法是调用基类的构造器

Rectangle(height,width);  (译注:2008/1/16更新 原代码的调用有误,应该是这样:Rectangle.call(this,height,width);   )

}

PositionedRectangle.prototype = new Rectangle;

var prObj = new PositionedRectangle(2,3,4,5);

alert(prObj.topLeftX+" "+ prObj.topLeftY+" "+ prObj.height+" "+ prObj.width);

这样的输出结果会是2 3 4 5。这里我们做调用子类的一个普通法一样调用基类构造器,来设置正确的值。

它是如何工作的?

1. 首先,new操作符会创建一个普通的对象,将它的prototype属性的值赋给PositionedRectangle.prototype.。

2. New操作符传入一个新创建的对象给PositionedRectangle构造器,用this关键字来表示它。

3. 接下来初始化topLeftX和topLeftX。

4. 在新创建的对象作用域里调用Rectangle方法,来初始化height和width的值。

5. 在PositionedRectangle构造器返回时,将新创建的对象赋值给prObj。

如果我们以后创建一个PositionedRectangle的子类,那么就需要为子类的prototype属性赋值为PositionedRectangle的实例,然后在子类的构造器里像调用普通方法一样调基类的构造器。

数据封装

所谓的数据封装,就是说对象的数据成员只能内部访问,而通过一些公用的方法来暴露给外界。简单的说,就是外界无法直接访问和修改对象的属性。

在Jscript中,默认情况下所有的成员都是公有的,不需要通过方法来访问和修改这些成员。像上面的例子:

ect1.height = 10; //会将height的值修改为10

那么我们该如何来封装成员呢?

答案就是能过使用内嵌函数,而不要在对象成员前加this关键字。让我们来为Rectangle增加一个colour属性。我们要将colour定义为私有的,而通过公有函数来向对界开放。

function Rectangle (ht, wt) {

                this.height = ht;

                this.width=wt;

                var colour = null;  // colour now is a private member of this class.

                this.setColour = setColour;

                this.getColour=getColour;

//These methods will be used to access / modify colour.

                function setColour (passedColour) {

                                colour =passedColour;

                }

                function getColour() {

                                return colour;

}             

}

ar rect1 = new Rectangle(5,10);

rect1.setColour (“blue”); // 设置颜色为blue.

alert(rect1.getColour); // 这里会弹出blue

alert(rect1.colour) // 这里会弹出undefined

Rect1.colour =”red”; /*这里并不会弹出任何错误,也不会修改原先的color属性的值,而是会在Rect1对象上创建一个公有的colour属性。 */

alert(rect1.getColour); //这里仍然会弹出blue

alert(rect1.colour) // 而这里则会弹出red.

这就是我们定义私有成员,和通过公有方法来访问私有成员的途径。这样我们就可以达到封装数据的目的了。

到这里所介绍的并不是在Jscript中实现类的全部,但是做为一个开始已经是足够了。希望这个博客对你是有用的,并且你也喜欢阅读。

这三篇就是我们计划在这个主题上的所有讨论了。这里还有一些有用的链接:

http://www.webdevelopersjournal.com/articles/jsintro3/js_begin3.html

http://dean.edwards.name/weblog/2006/03/base/

http://www.crockford.com/javascript/inheritance.html

http://devedge-temp.mozilla.org/viewsource/2001/oop-javascript/

谢谢,

Ritesh

SDET, JScript Team.

posted on 2007-11-12 14:19  阿不  阅读(2655)  评论(2编辑  收藏  举报