- 浏览: 73998 次
- 性别:
- 来自: 广州
文章分类
最新评论
转http://developer.51cto.com/art/200901/104775.htm
在开始设计模式的书写之前,有必要对Javascript面向对象的概念先做个介绍,那么这篇文章就以面向对象基础作为起点吧。
理论知识
1. 首先Javascript是弱类型语言,它定义变量时不必声明类型,如var Person = new Person(),它的变量类型为“var”,现在的C# 3.0也引进了这种匿名类型的概念,弱类型的变量产生了极大的灵活性,因为Javascript会根据需要来进行类型转换。所以这也决定了它采用了晚绑定的方法,即在运行后才知道变量的类型;
2. 面向对象概念不必多说,封装,继承,多态;
3. Javascript对象的类型主要分为三种:本地对象,如String,Array,Date等;内置对象,如Global,Math等;宿主对象,是指传统面向对象程序设计中的作用域,如公有,保护,私有,静态等等。
主要内容
1. 现在让我们来看看Javascript怎样创建对象的:
这样就创建了最简单的类和对象,其中我们可以把function Man() {} 看作是Man类的构造函数,getNickName()看作是Man类的方法,准确说可以“当作”是Man类的公共方法;为什么要说是当作呢?那是因为其实Javascript实际上并没有一个私有共有的划分,因此开发者们自己指定了这样的规约,那么规约是什么样的呢?我这里把Man类的清单完整地列出来:
这样的类是否看起来和传统的类很相似了呢?
2.接下来这个是本篇的一个重点,就是用Javascript如何设计一个接口,然后让类继承于它。
首先,先让我们看传统的C#语言是如何设计接口的吧:
接口中可以声明属性、方法、事件和类型(Structure),(但不能声明变量),但是并不能设置这些成员的具体值,也就是说,只能定义,不能给它里面定义的东西赋值,而接口作为它的继承类或者派生类的规约,继承类或者它的派生类能够共同完成接口属性、方法、事件和类型的具体实现,因为这里GetName(),SetName(),不管是方法名还是属性调用顺序上都是要保持一致的;
那么有了这样的一个基于接口的思想,我们设计Javascript的接口类的时候也需要考虑到这个规范。我先从主JS文件调用端开始说起:
其中Interface类是稍后要说的接口类,第一个参数"Person"是接口类的名称,第二个参数是个二维数组,"getName"是接口方法的名称,"0"是该方法所带的参数个数(因为Javascript是弱语言,所以类型是不确定的,所以只要记住参数个数就好,"0"可以省略不写),"setName"同理。这样一个接口定义好了。怎样使用它呢?
看到Man的构造函数里面包含
Interface.registerImplements(this, Person);
它是用来将实例化的this对象继承于Person接口,然后继承类对接口的方法进行实现。
代码看起来是不是很清晰和简单呢,那么现在要开始介绍真正的核心代码Interface.js了:
先看Interface的构造函数部分
刚才看到了var Person = new Interface("Person", [["getName", 0], ["setName", 1]]);,这里将两个参数分别保存起来;
刚才这句Interface.registerImplements(this, Person);,实际上这里是把this对象的方法名以及参数个数与刚Person保存的methods逐一进行比较,如果找不到或者不匹配,就警告错误;其中object[method].getParameters().length,调用了如下的代码:
getParrameters()方法作为Function对象的一个扩展,功能是取得方法含有的参数数组;
Interface.js完整的代码如下:
好了该创建一个html页面来试试效果了:
最终结果为:"Leepy"的弹出框。
这里还有一点要强调,如果接口上的方法没有在继承类上得到完全实现,或者方法参数个数不匹配,那么就会提示错误。
3. 如果我要一个类继承于另一个类该怎么做呢,继续看例子,这里我再定义一个SchoolBoy(男学生)类:
其中Man.call(this);实际上是将Man中的关键字this赋值于SchoolBoy对象中去,那么SchoolBoy就拥有了Man构造函数中的name属性了。
SchoolBoy.prototype = new Man();实际上是把Man的prototype赋值给SchoolBoy.prototype,那么SchoolBoy就有了Man类中的方法。
而后面跟着的getName(),setName(),实际上是覆盖了前面继承于Man类中的方法了。
然后看看效果:
最后结果为:"Mr 周杰伦's"的弹出框。
在开始设计模式的书写之前,有必要对Javascript面向对象的概念先做个介绍,那么这篇文章就以面向对象基础作为起点吧。
理论知识
1. 首先Javascript是弱类型语言,它定义变量时不必声明类型,如var Person = new Person(),它的变量类型为“var”,现在的C# 3.0也引进了这种匿名类型的概念,弱类型的变量产生了极大的灵活性,因为Javascript会根据需要来进行类型转换。所以这也决定了它采用了晚绑定的方法,即在运行后才知道变量的类型;
2. 面向对象概念不必多说,封装,继承,多态;
3. Javascript对象的类型主要分为三种:本地对象,如String,Array,Date等;内置对象,如Global,Math等;宿主对象,是指传统面向对象程序设计中的作用域,如公有,保护,私有,静态等等。
主要内容
1. 现在让我们来看看Javascript怎样创建对象的:
<script type="text/javascript"> function Man(){ } Man.prototype.getNickName=function() { return '爱在Cookies'; } var man = new Man(); alert(man.getNickName()); </script>
这样就创建了最简单的类和对象,其中我们可以把function Man() {} 看作是Man类的构造函数,getNickName()看作是Man类的方法,准确说可以“当作”是Man类的公共方法;为什么要说是当作呢?那是因为其实Javascript实际上并没有一个私有共有的划分,因此开发者们自己指定了这样的规约,那么规约是什么样的呢?我这里把Man类的清单完整地列出来:
<script type="text/javascript"> function Man() { // 私有静态属性 var sex='男'; //私有静态方法 function checkSex() { return (sex=='男'); } //私有方法 this._getSex=function() { if(checkSex()) { return '男'; }else { return '女'; } } //私有方法 this.getFirstName=function() { return '陈'; } //私有方法 this.getLastName=function() { return '顶强'; } } //公共方法 Man.prototype.getNickName=function() { return '爱在Cookies'; } //公共方法 Man.prototype.getFullName=function() { return this.getFirstName+this.getLastName; } //公共方法 Man.prototype.getSex=function() { return this._getSex(); } //公共静态方法 Man.say=function() { return 'Happy new Year!'; } </script>
这样的类是否看起来和传统的类很相似了呢?
2.接下来这个是本篇的一个重点,就是用Javascript如何设计一个接口,然后让类继承于它。
首先,先让我们看传统的C#语言是如何设计接口的吧:
public interface Person { string GetName(); void SetName(string name); } public class Man : Person { private string _name; public string GetName() { return _name; } public void SetName(string name) { _name = name; } }
接口中可以声明属性、方法、事件和类型(Structure),(但不能声明变量),但是并不能设置这些成员的具体值,也就是说,只能定义,不能给它里面定义的东西赋值,而接口作为它的继承类或者派生类的规约,继承类或者它的派生类能够共同完成接口属性、方法、事件和类型的具体实现,因为这里GetName(),SetName(),不管是方法名还是属性调用顺序上都是要保持一致的;
那么有了这样的一个基于接口的思想,我们设计Javascript的接口类的时候也需要考虑到这个规范。我先从主JS文件调用端开始说起:
<script type="text/javascript"> function Man() { this.name = ""; Interface.registerImplements(this, Person); } Man.prototype.getName = function() { return this.name; }; Man.prototype.setName = function(name) { this.name = name; }; </script>
其中Interface类是稍后要说的接口类,第一个参数"Person"是接口类的名称,第二个参数是个二维数组,"getName"是接口方法的名称,"0"是该方法所带的参数个数(因为Javascript是弱语言,所以类型是不确定的,所以只要记住参数个数就好,"0"可以省略不写),"setName"同理。这样一个接口定义好了。怎样使用它呢?
看到Man的构造函数里面包含
Interface.registerImplements(this, Person);
它是用来将实例化的this对象继承于Person接口,然后继承类对接口的方法进行实现。
代码看起来是不是很清晰和简单呢,那么现在要开始介绍真正的核心代码Interface.js了:
先看Interface的构造函数部分
<script type="text/javascript"> function Interface(name,methods) { if(arguments.length!=2) { throw new Error("接口构造函数含" + arguments.length + "个参数, 但需要2个参数."); } this.name=name; this.methods = []; if(this.methods.length<1) { throw new Error('第二个参数为空数组.'); } for(var i=0,len=methods.length;i<len;i++) { if(typeof methods[i][0] !== 'string') { throw new Error("接口构造函数第一个参数必须为字符串类型."); } if(methods[i][0] && typeof methods[i][1] !== 'number') { throw new Error("接口构造函数第二个参数必须为整数类型."); } if(methods[i].length=1) { methods[i][1]=0; } this.methods.push(methods[i]); } } </script>
刚才看到了var Person = new Interface("Person", [["getName", 0], ["setName", 1]]);,这里将两个参数分别保存起来;
Interface.registerImplements=function(object) { if(arguments.length<2) { throw new Error("接口的实现必须包含至少2个参数."); } for(var i=1,len=arguments.length;i<len;i++) { var interface=arguments[i]; if(interface.constructor != Interface ) { throw new Error("从第2个以上的参数必须为接口实例."); } for(var j = 0, methodsLen = interface.methods.length; j < methodsLen; j++) { var method = interface.methods[j][0]; if(!object[method] || typeof object[method] !== 'function' || object[method].getParameters().length != interface.methods[j][1]) { throw new Error("接口的实现对象不能执行" + interface.name + "的接口方法" + method + ",因为它找不到或者不匹配."); } } } }
刚才这句Interface.registerImplements(this, Person);,实际上这里是把this对象的方法名以及参数个数与刚Person保存的methods逐一进行比较,如果找不到或者不匹配,就警告错误;其中object[method].getParameters().length,调用了如下的代码:
Function.prototype.getParameters=function() { var str = this.toString(); var paramString = str.slice(str.indexOf('(') + 1, str.indexOf(')')).replace(/\s*/g,''); //取得参数字符串 try { return (paramString.length == 0 ? [] : paramString.split(',')); } catch(err) { throw new Error("函数不合法!"); } }
getParrameters()方法作为Function对象的一个扩展,功能是取得方法含有的参数数组;
Interface.js完整的代码如下:
function Interface(name, methods) { if(arguments.length != 2) { throw new Error("接口构造函数含" + arguments.length + "个参数, 但需要2个参数."); } this.name = name; this.methods = []; if(methods.length < 1) { throw new Error("第二个参数为空数组."); } for(var i = 0, len = methods.length; i < len; i++) { if(typeof methods[i][0] !== 'string') { throw new Error("接口构造函数第一个参数必须为字符串类型."); } if(methods[i][1] && typeof methods[i][1] !== 'number') { throw new Error("接口构造函数第二个参数必须为整数类型."); } if(methods[i].length == 1) { methods[i][1] = 0; } this.methods.push(methods[i]); } }; Interface.registerImplements = function(object) { if(arguments.length < 2) { throw new Error("接口的实现必须包含至少2个参数."); } for(var i = 1, len = arguments.length; i < len; i++) { var interface = arguments[i]; if(interface.constructor !== Interface) { throw new Error("从第2个以上的参数必须为接口实例."); } for(var j = 0, methodsLen = interface.methods.length; j < methodsLen; j++) { var method = interface.methods[j][0]; if(!object[method] || typeof object[method] !== 'function' || object[method].getParameters().length != interface.methods[j][1]) { throw new Error("接口的实现对象不能执行" + interface.name + "的接口方法" + method + ",因为它找不到或者不匹配."); } } } }; Function.prototype.getParameters = function() { var str = this.toString(); var paramString = str.slice(str.indexOf('(') + 1, str.indexOf(')')).replace(/\s*/g,''); //取得参数字符串 try { return (paramString.length == 0 ? [] : paramString.split(',')); } catch(err) { throw new Error("函数不合法!"); } }
好了该创建一个html页面来试试效果了:
最终结果为:"Leepy"的弹出框。
这里还有一点要强调,如果接口上的方法没有在继承类上得到完全实现,或者方法参数个数不匹配,那么就会提示错误。
3. 如果我要一个类继承于另一个类该怎么做呢,继续看例子,这里我再定义一个SchoolBoy(男学生)类:
function SchoolBoy(classNo, post) { Man.call(this); this._chassNo = classNo; this._post = post; } SchoolBoy.prototype = new Man(); SchoolBoy.prototype.getName = function() { return "Mr " + this.name; } SchoolBoy.prototype.setName = function(name) { this.name = name + "'s"; }
其中Man.call(this);实际上是将Man中的关键字this赋值于SchoolBoy对象中去,那么SchoolBoy就拥有了Man构造函数中的name属性了。
SchoolBoy.prototype = new Man();实际上是把Man的prototype赋值给SchoolBoy.prototype,那么SchoolBoy就有了Man类中的方法。
而后面跟着的getName(),setName(),实际上是覆盖了前面继承于Man类中的方法了。
然后看看效果:
var schoolboy = new SchoolBoy("三年二班", "班长"); schoolboy.setName("周杰伦"); alert(schoolboy.getName());
最后结果为:"Mr 周杰伦's"的弹出框。
发表评论
-
理解javascript的caller,callee,call,apply概念
2010-12-20 17:19 555在提到上述的概念之前 ... -
JavaScript事件的理解
2010-12-20 17:01 621在很多语言的学习中,“事件”都是一个比较难理解,但是又是一个很 ... -
javascript事件机制
2010-12-20 16:28 1158转http://www.jb51.net/article/22 ... -
offsetLeft,Left,clientLeft的区别
2010-12-20 12:12 609假设 obj 为某个 HTML 控件。 obj.offs ... -
Jquery1.4.3源码分析(一)
2010-12-17 16:19 1025Jquery是站在开发者的角度去考虑问题,在使用Js的时候,大 ... -
JavaScript的5种调用函数的方法
2010-12-17 14:52 649转http://www.cnblogs.com/lhb25 ... -
nodeType属性
2010-12-16 09:30 694作 用 辨识节点的DOM 型态。 基本语法 numN ... -
JavaScript exec() 方法
2010-12-16 09:18 715转http://www.w3school.com.cn/js/ ... -
JavaScript 的命名空间
2010-12-15 23:50 1029JavaScript 的命名空间并 ... -
window.showModalDialog弹出框
2010-12-15 16:08 603当我们在使用window.showModalDialog弹出框 ... -
javascript调用文件保存命令
2010-12-14 14:31 885使用javascript调用系统文件保存 if(confi ...
相关推荐
Javascript面向对象特性实现(封装、继承、接口 Javascript面向对象特性实现(封装、继承、接口
第8章 框架与重用:使用接口和抽象类实现设计 119 8.1 代码:重用还是不重用 119 8.2 什么是框架 119 8.3 什么是契约 121 8.3.1 抽象类 122 8.3.2 接口 124 8.3.3 集成 125 8.3.4 编译器的证明 127 8.3.5 ...
本文实例讲述了JavaScript面向对象三个基本特征。分享给大家供大家参考,具体如下: 了解过面向对象的同学应该都知道,面向对象三个基本特征是:封装、继承、多态,但是对于这三个词具体可能不太了解。对于前端来讲...
dejavu 主要特性:类(具体的、抽象的、final类)接口混入(这样你可以使用某种形式的多重继承)私有成员和受保护成员静态成员常量函数上下文绑定方法签名检查扩展和借用vanilla类自定义instanceOf,支持接口两个...
面向对象:对象作为程序的基本单元,程序分解为数据和相关操作 二、类、对象 类:对具有相同特性和特征事物的抽象描述 对象:某种类型对应的具体事物 三、面向对象的三大特性 封装:隐藏实现细节,实现代码模块化 继承...
一般的面向对象程序语言,有两种继承方法——接口继承(interface inheritance)和实现继承(implementation inheritance)。接口继承只继承方法签名,而实现继承则继承实际的方法。在JavaScript中,函数没有签名,...
七巧板一个用于 JavaScript 中无类面向对象编程的库。 我曾经认为 JavaScript 的重要创新是原型继承。 我现在认为它是无类的面向对象编程。 我认为这是 JavaScript 给人类的礼物。 这就是使它真正有趣、特别和重要的...
源码中并未采取面向接口、继承等思想来实现工具方法的复用,笔者认为不应该将编程语言的学习成本带入数据结构,笔者的意愿是:学习者拿到每个数据结构,都可以做到信手使用,而不是还要反复查询其继承、实现结构。
第一部分给出了实现具体设计模式所需要的面向对象特性的基础知识,主要包括接口、封装和信息隐藏、继承、单体模式等内容。第二部分则专注于各种具体的设计模式及其在JavaScript语言中的应用,主要介绍了工厂模式、...
类和继承带上onExtend()和onInitialize()钩子等等 界面用于文档隐式接口和验证对象 方法重载帮助您清理函数标题行中参数的类型声明 类似关键字的功能简化复杂表达式和重复代码的一系列功能 安装 安装nodejs npm...
类,抽象类,接口,继承,类属性/字段,类方法,类构造函数,类常量,类事件,抽象类成员,属性类型,方法参数类型,方法返回值类型,方法重载,可选方法参数,自动加载类和接口以及反射API。 我很想听到任何有兴趣...
第一部分给出了实现具体设计模式所需要的面向对象特性的基础知识,主要包括接口、封装和信息隐藏、继承、单体模式等内容。第二部分则专注于各种具体的设计模式及其在JavaScript语言中的应用,主要介绍了工厂模式、...
比喻.js概述Trope 是一个简单的 JavaScript 继承接口,提供了一些额外的、有用的功能。 该工具的核心是用于维护、允许访问以及创建具有对象的原型。 开始与大多数项目集成很容易,因为构造函数、对象原型和 ES6 类...
继承是面向对象编程中又一非常重要的概念,JavaScript支持实现继承,不支持接口继承,实现继承主要依靠原型链来实现的
整个Extjs框架都是以一种面向对象的方式开发的,所以理解Javascript中的继承也很重要。我前面的一篇文章 补点基础:Javascript中的类和闭包 也是为这篇做准备。另外,博客园内还有一个写的很好的系列 JavaScript继承...
面向对象的分析、设计和编程 23 面向对象语言的发展简史 26 内容总结 29 独立实践 30 第三章:面向对象的程序设计 31 学习目标 31 类和对象的描述 32 声明类 32 声明属性 33 声明成员方法 34 源文件的布局 36 包的...
1、面向对象的特征有哪些方面? 8 2、作用域public,private,protected,以及不写时的区别? 8 3、String 是最基本的数据类型吗? 8 4、float 型float f=3.4是否正确? 8 5、语句float f=1.3;编译能否通过? 8 6、short ...
5.4.1 面向过程编程和面向对象编程 5.4.2 JavaScript的面向对象编程 5.4.3 用面向对象方式重写代码 5.5 其他问题 5.5.1 prototype和内置类 5.5.2 标签的自定义属性 5.5.3 标签的内联事件和event对象 5.5.4 ...