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
来访问这个成员了。
想要的任何形式。
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
来访问这个成员了。
相关文章推荐
- javascript面向对象开发
- javaScript 面向对象-继承(二)
- 令人惊奇的JavaScript面向对象(二)
- JavaScript面向对象的支持(6)
- 详解JavaScript基于面向对象之创建对象(1)
- JavaScript面向对象的程序设计2(组合构造和原型 继承)
- JavaScript面向对象的支持(7)
- JavaScript面向对象之继承和多态
- JavaScript面向对象(01)--函数
- javaScript 面向对象-原形prototype
- JavaScript的语法要点 4 - 面向对象的基础
- javascript 经典案例 面向对象的选项卡
- 《JavaScript设计模式与开发》笔记 1.面向对象的JavaScript
- 详解JavaScript基于面向对象之继承实例
- javascript面向对象3
- JavaScript面向对象(一)——JS OOP基础与JS 中This指向详解
- JavaScript面向对象二
- javascript面向对象,实现namespace,class,继承,重载
- JavaScript面向对象的继承机制实现方式
- javascript—面向对象