阿不

潜水

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

【原文地址】 Classes in Jscript - Part I

【原文发表日期】 Monday, September 24, 2007 9:23 AM by don.raman

我是Ritesh Parikh,最近刚转入Jscript Team作一名SDET(译注:我也不知道SDET是什么样的岗位:()。我刚接触Jscript时,我有很多的疑问。其中一个就是 Jscript是否支持面向对象?(译注:这里的Jscript是MS所实现的Javascript,由于MS的Jscript完全兼容于Javascript 1.5,并在此基础上,针对IE做了一些扩展。这个系列里,介绍的内容实际上是Javascript 1.5中对面向对象支持。因此这些内容适合于我们目前广泛意义上的Javascript)

通常情况下,一种语言要支持面向对象的能力,必须具备以下的能力:

l 一种将相关函数组织在一个模块中,并封装他们实现细节的机制。比如:类。

l 一种重用函数,广泛意义上的继承机制。

l 一种逻辑上做相同事情,但却有不同实现的机制。我们通常称之为多态机制。

JScript支持以上所有的机制。在这篇博客中,我将会介绍Jscript是如何支持定义一个类的。

Jscript并不支持一般意义上的“类”,这就决定了它与传统上的面向对象语言,如C++或者JAVA有很大的不同。Jscript不支持传统意义上的类继承,取而代之的是一种利用prototype技术来实现(模拟)类的继承。

如果Jscript不支持一般意义上的类,那它是如何来支持定义“类”,并创建实例的呢?这就是我这篇博客将要解释的内容。

Jscript中并不支持与传统语言一模一样的类的概念,但是它使用构造器函数(constructor function)和prototype对象来模拟“类”。

让我们先来定义一个这篇博客中将要用到的几个关键词:

类(class):在传统的面向对象语言中,它是定义了对象的结构,包括对象包括的属性,属性的类型和对象的操作方法。在Jscript中,并没有这种类型的定义。(译注:在Jscript中并没有直接支持定义这样的类)

构造器函数(Constuctor functions):能够被new关键字初始化的函数称为构造器函数或简称为构造器。它的工作就是初始化一个带任意属性的对象,这个对象在使用new关键字后被返回。(译注:我不也不清楚该如何翻译这个概念,它可以这样理解:它本身就是一个函数,同时它可以被认为是一个对象的构造器。)

Prototype对象:在Jscript中,除了Object.prototype,每个对象(类型层面上,不是实例)都有带有一个被称为prototype对象的引用。在prototype里的所有属性(和方法),都会出现在每个对象的实例中。(译注:理解prototype的概念,对理解Jscript中的继承和性能提升有很重要的意义。)

带着这些信息,我们来开始使用Jscript来实现第一个“类”。

我们要来定义一个带有带有高和宽的矩形,并且要提供一个计算其面积的area方法。

首先,我们要来定义一个类,但Jscript中不支持Class关键字。那我们该如何做呢?看下面的代码。

定义一个构造器函数来初始所需的数据:

function Rectangle (ht, wt) {

            this.height = ht;

            this.width=wt;

}

接下来是创建Rectangle对象

var rect1 = new Rectangle(5,10);

var rect2 = new Rectangle(2,4);

然后定义一个函数来计算Rectangle的面积

function computeArea(rObj) {

            return rObj.height * rObj.width;

}

调用该函数来计算面积:

var rectArea = computeArea(rect1); //will return 50.

但是这样做就不是一个严格意义上的面向对象了。一种更好的办法是在Rectangle对象中调用一个函数来直接返回它的面积。在类中添加一个函数是可行的,加上这个函数的话,Rectangle的构造器变成了这样:

function Rectangle (ht, wt) {

            this.height = ht;

            this.width=wt;

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

}

然后我们就可以这样调用计算它的面积了:

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

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

但这样还不是一个最理想的面向对象的实现,还有更好的方式。在这里矩形对象中会有会个属性,height, width和area。height, width对于每个矩形对象实例来说,可能会有不同的值,但是area对于每个对象实例来说使用的都是相同的函数原形。这就意味着area方法对每个矩形对象实例来说是可以共享的,那我们为什么不那么做呢?

我们可以做这张图片中得知prototype对象源来。(译注:图?)

我们把area定义为Rectangle的一个属性:

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

rect1.area(); // will return 50.

rect2.area(); // will return 8.

从上面我们所做的可以总结出以下的一些信息:

构造器会为类提供一个名称,和初始化每一个类的实现的属性。Prototype对象与构造器有关,prototype对象里定义的任何属性都被每个由于构造器实例化的对象所继承。因此prototype对象是一个最理想地方来存储像area这个所有对象都相同的属性。(常量)

这样做具有以下一些优点:

所有相同的方法(一次定义所有实例共享)被定义在prototype对象中可以被所有的实例共享。它可以大大减小内存的开销。

当一个属性在对象实例被创建之后被加在prototype对象中。那么这个对象实例同样也会继承这些新加的属性。

尽管所有的对象都能顺利的从prototype对象中读取属性,但是却无法修改它们,为什么会这样呢?

如果通过rect1(实例)我们可以修改area属性,那么势必其它的对象实例(像rect2, rect3…)在调用area属性时也一样会受到影响。尽管如此,我们仍然可以在对象的prototype的生命周期内来修改它本身的属性,但不是在对象的实例上。当我们试图在对象的实例上修改prototype属性时,它的结果是在对象实例中动态的添加这个属性以覆盖prototype的相同属性。

这只是一个开始,在接下来的文章中,我会来讨论实例属性和静态属性(类属性),实体方法和静态方法(类方法)。此外,我们还会来研究一下数据是如何被封装和继承的。

希望你喜欢这篇博客。

Thanks,

Ritesh

SDET, JScript Team

阿不 译

posted on 2007-10-15 14:52  阿不  阅读(1421)  评论(6编辑  收藏  举报