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

JavaScript面向对象(一)

2012-04-05 21:57 183 查看
JavaScript本身是基于对象的,而并非基于类。但是,JavaScript的函数式语言的特性使得它本身是可编程的,他可以变成你
想要的任何形式。

1.原型继承

JavaScriipt中的继承可以通过原型链来实现,调用对象上的一个方法,由于方法在JavaScript对象中是对另一个函数对象的

引用,因此解释器会在对象中查找该属性,如果没有找到,则在其内部对象protopype对象上搜索,由于prototype对象与对象本身

的结构是一样的,因此这个过程会一直回溯到发现该属性,则调用该属性,否则,报告一个错误。

functionBase( ) {

this.baseFunc= function (){

print ("base behavior");

}

}

function Middle( ){

this.middleFunc= function () {

print ("middle behavior");

}

}

Middle.prototype= new Base ();

functionFinal() {

this.finalFunc = function () {

print ("final behavior");

}

}

Final.prototype= new Middle( );

functiontest ( ) {

var obj = new Final();

obj.baseFunc();

obj.middleFunc();

obj.finalFunc();

}

在function test 中,我们new 了一个Final对象,然后依次调用obj.baseFunc,由于obj对象上并无此方法,则按照从Fianl->Middle->Base的规

则,进行回溯,在其原形链上搜索,由于Final的原型链包含Middle,而Middle上又包含Base,因此会执行这个方法,这样就实现了类的继承

basebehavior

middlebehavior

finalbehavior

2.引用

JS中的引用始终指向最终的对象,而并非引用本身。

varobj = { } ; //空对象

varref =obj; //引用

obj.name = "objectA";

print(ref.name); //ref跟着添加了name属性

obj= ["one","two","three"]; //obj指向了另一个对象(数组对象)

print(ref.name); //ref还指向原来的对象

print(obj.length); //3

print(ref.length); //undefined

运行结果如下:

objectA

objectA

3

undefined

obj只是一个匿名对象的引用,所以,ref并非指向它,当obj指向另一个数组对象时,可以看到,引用ref并未改变,而始终指向这那个

后来添加了name属性的"空"对象"{}"

varobj = { }; //新建一个对象,并被obj引用

varref1 =obj; //ref1引用obj,事实上是引用obj引用的空对象

varref2 =obj;

obj.func="function";

print(ref1.func);

print(ref2.func);

声明一个对象,然后用两个引用来引用这个对象,然后修改原始的对象,运行之:

function

function

根据运行结果我们可以看出,在定义了引用之后,修改原始的那个对象会影响到其应用上。

3. new 操作符

有面向对象编程的基础有时会成为一种负担,比如看到new的时候,java程序员可能会认为这将会调用一个类的构造器构造一个

新的对象出来。

function Shape(type){

this.type = type || "rect";

this.calc = function () {

return "calc," + this.type;

}

}

var triangle =new Shape("truangle");

print(triangle.calc());

var circle = new Shape("circle");

print(circle.calc());

运行结果如下:

calc,triangle

calc,circle

java程序员可能会觉得Shape就是一个类,然后triangle,circle既是Shape对应的具体对象,而其实JavaScript并非如此工作的,

罪魁祸首即为此new操作符。在JavaScript中,通过new 操作符来作用于一个函数,实质上会发生这样的动作:

首先,创建一个空对象,然后用函数的apply方法,将这个空对象传入作为apply的第一个参数,及上下文参数。这样函数内部的

this将会被这个空的对象所替代:

var triangle = new Shape("triangle");

//上一句相当于下面的代码

var triangle = {};

Shape.apply(triangle,["triangle"]);

4.封转

我们可以通过JavaScript的函数实现封转,封装的好处在于未经授权的客户代码无法访问到我们不公开的数据

function Person(name) {

//private variable

var address ="The Earth";

//public method

this.getAddress = function () {

return address;

}

//public variable

this.name = name;

}

//public

Person.rototype.getName = function() {

return this.name;

}

//public

Person.prototype.setName = function (name) {

this.name = name;

}

首先声明一个函数,作为模板,用面向对象的属于来讲,就是一个类。用var方式声明的变量仅在类内部可见,所以address为一个

私有成员,访问address的唯一方法是通过我们向外暴露的getAddress方法,而get/setName,均为原形链上的方法,因此为公开的。

我们可以测试如下:

var jack = new Person("jack");

print(jack.name);//jack

print(jack.getName()); //jack

print(jack.address); //undefined

print(jack.getAddress); //The Earth

直接通过jack.address来访问address变量会得到undefined。我们只能通过jack.getAddress来访问。这样,address这个成员就被封转起来了。

另外需要注意的是,我们可以为类添加静态成员,这个过程也很简单,只需要为函数对象添加一个属性即可。

function Person(name) {

//private variable

var address ="The Earth";

//public method

this.getAddress =function () {

return address;

}

//public variable

this.name =name;

}

Person.TAG ="JAVASCRIPT-CORE" ; //静态变量

print(Person.TAG);

也就是说,我们在访问Person.TAG时,不需要实例化Person类。这与传统的面向对象语言如Java中的静态变量是一致的。

4.工具包Base

Base是一个JavaScript的面向对象的基础包,Base本身很小,只有140行,但这个很小的包对面向对象编程风格有很好的支持,

支持类的定义,封装,继承,子类调用父类的方法等,代码的质量也很高,而且很多项目都在使用Base作为底层的支持。尽管如此,

JavaScript的面向对象风格依旧非常古怪,并不可以完全和传统的OO语言对等起来。

几个基于Base的例子,假设我们现在在开发一个任务系统,我们需要抽象出一个类来表示任务,对应的,每个任务都可能会有一

个监听器,当任务执行之后,需要通知监听器。我们首先定义一个事件监听器的类,然后定义一个任务类:

var EventListener =Base.extend({

constructor :function(sence){

this.sense =sense;

},

sense:null,

handle:function(){

print(this.sense+"occured");

}

});

var Task =Base.extend({

constructor: function(name){

this.name=name;

},

name:null,

listener:null,

execute: function(){

print(this.name);

this.listener.handle();

},

setListener: function(listener){

this.listener =listener;

}

});

创造类的方式很简单,需要给Base.extend方法传入一个JSON对象,其中可以有成员和方法。方法访问自身的成员时需要

加this关键字。而每一个类都会有一个constructor的方法,即构造方法。比如事件监听器类(EventListener)的构造器需要传入一

个字符串,而任务类(Task)也需要传入任务的名字来进行构造。有了任务类和事件监听器类,我们来实例化它们:

var printing= newTask("printing");

var printEventListener = newEventListener("printing");

printing.setListener(printEventListener);

printing.execute();

首先,创建一个新的Task,做打印工作,然后新建一个事件监听器,并将它注册在新建的任务上,这样,当打印发生时,会通知

监听器,监听器会做出相应的判断:

printing

printing occurred

既然有了基本的框架,我们就来使用这个框架,假设我们要从HTTP服务器上下载一个页面,于是我们设计了一个新的任务类型,

叫做HttpRequester:

var HttpRequest =Task.extend({

constructor:function(name,host.port){

this.base(name);

this.host=host;

this.port =port;

},

host:"127.0.0.1",

port:9527,

execute:function(){

print("["+this.name+"]request send to" +this.host+"of port"+this.port );

this.listener.handle();

}

});

HttpRequester类继承了Task,并且重载了Task类的execute方法,setListener方法的内容与父类一致,因此不需要重载;

var requester = newHttpRequester("requseter1","127.0.0.1",8752);

var listener = newEventListener("http_request");

requester.setListener(listener);

requester.execute();

我们新建一个HttpRequster任务,然后注册上事件监听器,并执行之:

[requester1]request send to 127.0.0.1of port 8752

http_request occured

应该注意到HttpRequester类的构造器中,有这样一个语句:

this.base(name);

表示执行父类的构造器,即将name赋值给父类的成员变量name,这样在HttpRequester的实例中,我们就可以通过this.name

来访问这个成员了。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: