分享
 
 
 

&#106avascript面向对象的支持(4)

王朝html/css/js·作者佚名  2006-11-24
窄屏简体版  字體: |||超大  

================================================================================

Qomolangma OpenProject v0.9

类别 :Rich Web Client

关键词 :JS OOP,JS Framwork, Rich Web Client,RIA,Web Component,

DOM,DTHML,CSS,JavaScript,JScript

项目发起:aimingoo (aim@263.net)

项目团队:aimingoo, leon(pfzhou@gmail.com)

有贡献者:JingYu(zjy@cnpack.org)

================================================================================

八、JavaScript面向对象的支持

~~~~~~~~~~~~~~~~~~

(续)

3. 构造、析构与原型问题

--------

我们已经知道一个对象是需要通过构造器函数来产生的。我们先记住几点:

- 构造器是一个普通的函数

- 原型是一个对象实例

- 构造器有原型属性,对象实例没有

- (如果正常地实现继承模型,)对象实例的constructor属性指向构造器

- 从三、四条推出:obj.constructor.prototype指向该对象的原型

好,我们接下来分析一个例子,来说明JavaScript的“继承原型”声明,以

及构造过程。

//---------------------------------------------------------

// 理解原型、构造、继承的示例

//---------------------------------------------------------

function MyObject() {

this.v1 = 'abc';

}

function MyObject2() {

this.v2 = 'def';

}

MyObject2.prototype = new MyObject();

var obj1 = new MyObject();

var obj2 = new MyObject2();

1). new()关键字的形式化代码

------

我们先来看“obj1 = new MyObject()”这行代码中的这个new关键字。

new关键字用于产生一个实例(说到这里补充一下,我习惯于把保留字叫关键字。

另外,在JavaScript中new关键字同时也是一个运算符),但这个实例应当是从

一个“原型的模板”复制过来的。这个用来作模板的原型对象,就是用“构造器

函数的prototype属性”所指向的那个对象。对于JavaScript“内置对象的构造

器”来说,它指向内部的一个原型。

每一个函数,无论它是否用作构造器,都会有一个独一无二的原型对象。缺省时

JavaScript用它构造出一个“空的初始对象实例(不是null)”。然而如果你给函

数的这个prototype赋一个新的对象,那么构造过程将用这个新对象作为“模板”。

接下来,构造过程将调用MyObject()来完成初始化。——注意,这里只是“初始

化”。

为了清楚地解释这个过程,我用代码形式化地描述一下这个过程:

//---------------------------------------------------------

// new()关键字的形式化代码

//---------------------------------------------------------

function new(aFunction) { // 如果有参数args

var _this = aFunction.prototype.clone(); // 从prototype中复制一个对象

aFunction.call(_this); // 调用构造函数完成初始化, (如果有,)传入args

return _this; // 返回对象

}

所以我们看到以下两点:

- 构造函数(aFunction)本身只是对传入的this实例做“初始化”处理,而

不是构造一个对象实例。

- 构造的过程实际发生在new()关键字/运算符的内部。

而且,构造函数(aFunction)本身并不需要操作prototype,也不需要回传this。

2). 由用户代码维护的原型(prototype)链

------

接下来我们更深入的讨论原型链与构造过程的问题。这就是:

- 原型链是用户代码创建的,new()关键字并不协助维护原型链

以Delphi代码为例,我们在声明继承关系的时候,可以用这样的代码:

//---------------------------------------------------------

// delphi中使用的“类”类型声明

//---------------------------------------------------------

type

TAnimal = class(TObject); // 动物

TMammal = class(TAnimal); // 哺乳动物

TCanine = class(TMammal); // 犬科的哺乳动物

TDog = class(TCanine); // 狗

这时,Delphi的编译器会通过编译技术来维护一个继承关系链表。我们可以通

过类似以下的代码来查询这个链表:

//---------------------------------------------------------

// delphi中使用继关系链表的关键代码

//---------------------------------------------------------

function isAnimal(obj: TObject): boolean;

begin

Result := obj is TAnimal;

end;

var

dog := TDog;

// ...

dog := TDog.Create();

writeln(isAnimal(dog));

可以看到,在Delphi的用户代码中,不需要直接继护继承关系的链表。这是因

为Delphi是强类型语言,在处理用class()关键字声明类型时,delphi的编译器

已经为用户构造了这个继承关系链。——注意,这个过程是声明,而不是执行

代码。

而在JavaScript中,如果需要获知对象“是否是某个基类的子类对象”,那么

你需要手工的来维护(与delphi这个例子类似的)一个链表。当然,这个链有不

叫类型继承树,而叫“(对象的)原型链表”。——在JS中,没有“类”类型。

参考前面的JS和Delphi代码,一个类同的例子是这样:

//---------------------------------------------------------

// JS中“原型链表”的关键代码

//---------------------------------------------------------

// 1. 构造器

function Animal() {};

function Mammal() {};

function Canine() {};

function Dog() {};

// 2. 原型链表

Mammal.prototype = new Animal();

Canine.prototype = new Mammal();

Dog.prototype = new Canine();

// 3. 示例函数

function isAnimal(obj) {

return obj instanceof Animal;

}

var

dog = new Dog();

document.writeln(isAnimal(dog));

可以看到,在JS的用户代码中,“原型链表”的构建方法是一行代码:

"当前类的构造器函数".prototype = "直接父类的实例"

这与Delphi一类的语言不同:维护原型链的实质是在执行代码,而非声明。

那么,“是执行而非声明”到底有什么意义呢?

JavaScript是会有编译过程的。这个过程主要处理的是“语法检错”、“语

法声明”和“条件编译指令”。而这里的“语法声明”,主要处理的就是函

数声明。——这也是我说“函数是第一类的,而对象不是”的一个原因。

如下例:

//---------------------------------------------------------

// 函数声明与执行语句的关系(firefox 兼容)

//---------------------------------------------------------

// 1. 输出1234

testFoo(1234);

// 2. 尝试输出obj1

// 3. 尝试输出obj2

testFoo(obj1);

try {

testFoo(obj2);

}

catch(e) {

document.writeln('Exception: ', e.description, '<BR>');

}

// 声明testFoo()

function testFoo(v) {

document.writeln(v, '<BR>');

}

// 声明object

var obj1 = {};

obj2 = {

toString: function() {return 'hi, object.'}

}

// 4. 输出obj1

// 5. 输出obj2

testFoo(obj1);

testFoo(obj2);

这个示例代码在JS环境中执行的结果是:

------------------------------------

1234

undefined

Exception: 'obj2' 未定义

[object Object]

hi, obj

------------------------------------

问题是,testFoo()是在它被声明之前被执行的;而同样用“直接声明”的

形式定义的object变量,却不能在声明之前引用。——例子中,第二、三

个输入是不正确的。

函数可以在声明之前引用,而其它类型的数值必须在声明之后才能被使用。

这说明“声明”与“执行期引用”在JavaScript中是两个过程。

另外我们也可以发现,使用"var"来声明的时候,编译器会先确认有该变量

存在,但变量的值会是“undefined”。——因此“testFoo(obj1)”不会发

生异常。但是,只有等到关于obj1的赋值语句被执行过,才会有正常的输出。

请对照第二、三与第四、五行输出的差异。

由于JavaScript对原型链的维护是“执行”而不是“声明”,这说明“原型

链是由用户代码来维护的,而不是编译器维护的。

由这个推论,我们来看下面这个例子:

//---------------------------------------------------------

// 示例:错误的原型链

//---------------------------------------------------------

// 1. 构造器

function Animal() {}; // 动物

function Mammal() {}; // 哺乳动物

function Canine() {}; // 犬科的哺乳动物

// 2. 构造原型链

var instance = new Mammal();

Mammal.prototype = new Animal();

Canine.prototype = instance;

// 3. 测试输出

var obj = new Canine();

document.writeln(obj instanceof Animal);

这个输出结果,使我们看到一个错误的原型链导致的结果“犬科的哺乳动

物‘不是’一种动物”。

根源在于“2. 构造原型链”下面的几行代码是解释执行的,而不是象var和

function那样是“声明”并在编译期被理解的。解决问题的方法是修改那三

行代码,使得它的“执行过程”符合逻辑:

//---------------------------------------------------------

// 上例的修正代码(部分)

//---------------------------------------------------------

// 2. 构造原型链

Mammal.prototype = new Animal();

var instance = new Mammal();

Canine.prototype = instance;

3). 原型实例是如何被构造过程使用的

------

仍以Delphi为例。构造过程中,delphi中会首先创建一个指定实例大小的

“空的对象”,然后逐一给属性赋值,以及调用构造过程中的方法、触发事

件等。这个过程跟JavaScript中的行为是一致的:

//----------------------------------------------------

[1] [2] 下一页

 
 
 
免责声明:本文为网络用户发布,其观点仅代表作者个人观点,与本站无关,本站仅提供信息存储服务。文中陈述内容未经本站证实,其真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
2023年上半年GDP全球前十五强
 百态   2023-10-24
美众议院议长启动对拜登的弹劾调查
 百态   2023-09-13
上海、济南、武汉等多地出现不明坠落物
 探索   2023-09-06
印度或要将国名改为“巴拉特”
 百态   2023-09-06
男子为女友送行,买票不登机被捕
 百态   2023-08-20
手机地震预警功能怎么开?
 干货   2023-08-06
女子4年卖2套房花700多万做美容:不但没变美脸,面部还出现变形
 百态   2023-08-04
住户一楼被水淹 还冲来8头猪
 百态   2023-07-31
女子体内爬出大量瓜子状活虫
 百态   2023-07-25
地球连续35年收到神秘规律性信号,网友:不要回答!
 探索   2023-07-21
全球镓价格本周大涨27%
 探索   2023-07-09
钱都流向了那些不缺钱的人,苦都留给了能吃苦的人
 探索   2023-07-02
倩女手游刀客魅者强控制(强混乱强眩晕强睡眠)和对应控制抗性的关系
 百态   2020-08-20
美国5月9日最新疫情:美国确诊人数突破131万
 百态   2020-05-09
荷兰政府宣布将集体辞职
 干货   2020-04-30
倩女幽魂手游师徒任务情义春秋猜成语答案逍遥观:鹏程万里
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案神机营:射石饮羽
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案昆仑山:拔刀相助
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案天工阁:鬼斧神工
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案丝路古道:单枪匹马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:与虎谋皮
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:李代桃僵
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:指鹿为马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:小鸟依人
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:千金买邻
 干货   2019-11-12
 
推荐阅读
 
 
 
>>返回首頁<<
 
靜靜地坐在廢墟上,四周的荒凉一望無際,忽然覺得,淒涼也很美
© 2005- 王朝網路 版權所有