JavaScript面向对象中接口实现方法详解


Posted in Javascript onJuly 24, 2019

本文实例讲述了JavaScript面向对象中接口实现方法。分享给大家供大家参考,具体如下:

接口是面向对象编程的基础,它是一组包含了函数型方法的数据结构,与类一样,都是编程语言中比较抽象的概念。比如生活中的接口,机顶盒,人们利用它来实现收看不同频道和信号的节目,它犹如对不同类型的信息进行集合和封装的设备,最后把各种不同类型的信息转换为电视能够识别的信息。在编程语言中的接口,实际上是不同类的封装并提供统一的外部联系通道,这样其他对象就可以利用接口来调用不同类的成员了。

——整理自《jQuery开发从入门到精通》

接口的概念

构造函数(类)是具体的实现,接口是类的约定。API接口(应用程序接口)、人机交互接口、电源接口、USB接口等虽然用途不同,功能各异,但是都包含一个共同的特性:约定、规范。可以说,接口就是一张契约和合同,它约定了设计者和使用者都必须遵循的要求。

接口承诺了具体类应该事先的功能。

举一个很有必要的例子,在java中实现接口,比如下面的代码:

interface Base{
 void function1();
 void function2();
 void function3();
}

Base接口承诺了3个基本功能:function1()、function2()、function3()。这个接口就像是一份合同,在甲方(调用类的用户)和乙方(定义类的开发人员)之间约定。

乙方负责实现接口约定的功能。功能的实现就是所谓的类。如下实例:

class App implements Base  // 定义一个App类,用这个类来实现接口Base

类App将遵照接口的约定。专业来说就是,应用类App继承Base接口类。

它的具体实现如下:

class App implements Base{
 void function1(){
  System.out.println("I am fun1");
 }
 void function2(){
  System.out.println("I am fun2");
 }
 void function3(){
  System.out.println("I am fun3");
 }
}

这样,乙方实现了这个接口,而甲方也应该来按照接口的约定去使用类App就行了。

所以说,接口(interface)和类(class),实际上都是相同的数据结构。

在接口中可以声明属性,方法,事件,类型,但不能声明变量,且不能设置被声明成员的具体值(功能实现)。

也就是说,接口只能定义成员,不能给定义的成员赋值。而接口作为它的继承类或派生类的约定,继承类或派生类共同完成接口属性、方法、事件、类型的实现。在接口和实现类之间,不管是方法名还是属性调用顺序上都应保持一致。

接口的目的就是约束编码,促使代码规范,对于强类型语言是必须的,也是非常重要的环节。但是对于JavaScript弱类型语言来说,严格的类型检查会束缚JavaScript的灵活性。很多前端开发人员根本不用接口,但不会影响脚本的设计。

使用接口的好处:降低对象间的耦合度,提高代码的灵活性。学会使用接口,能够让手中的函数变得灵巧,这在大型开发中是很重要的。

对于JavaScript来说,本身不支持接口功能,没有提供内置方法。但是人工设计一个额外的接口程序,又会对程序的性能产生影响。项目越大,这种开销越大。所以,用不用接口可以遵循两个条件:

① 项目大小,如果是一个框架,使用接口在一定程度上会提高程序的性能。如果是简单的应用,就不必使用接口了。

② 如果对JavaScript接口比较熟练,多用接口也可以,如果担心过多使用接口影响性能,则可以在考虑产品发布前,清除接口功能模块,或者设置接口的执行条件。防止它被频繁执行,影响性能。

接口的实现

JavaScript中并不支持接口,但是我们可以模仿其他语言,来定义接口。

下面来规划接口结构的设计和检测功能的实现:

(1) 设计一个接口辅助的类结构,这个构造函数相当于一个过滤器,用于在接口实例化过程中,检测初始化参数是否合法。如果符合接口设计标准,则把第2个参数中每个方法名和参数个数以数组元素的形式输入接口内部属性methods。在输入前分别检测每个参数类型是否符合规定。同时检查参数是否存在残缺,并即时以0补齐参数。

function Interface(name,methods){ // 接口辅助类,参数包括接口实例的名称和方法集
 if(arguments.length!=2){ // 如果参数个数不等于2,抛出异常。
  throw new Error('标准接口约定,需要两个参数');
 }
 this.name = name; // 存储第一个参数值,实例化后就是接口实例的名称
 this.methods = []; // 接口实例的方法存储器
 if(methods.length < 1){ // 如果第二个参数的元素个数为0,说明是空数组,抛出异常。
  throw new Error('接口的第二个参数不能为空');
 }
 for(var i = 0; i < methods.length; i++){ // 开始对第2个参数的元素进行遍历检测
  var item = methods[i];
  if(typeof item[0] !== 'string') { // 如果第二个参数的第一个元素不是string类型,抛出异常
   throw new Error("接口约定的第一个参数应为字符串");
  }
  if(item[1]&&typeof item[1] !== 'number'){ // 如果第二个参数有第二个元素,且第二个元素不是number类型,抛出异常
   throw new Error('接口约定的第个参数应为数值');
  }
  if(item.length == 1){ // 如果第二个参数只有一个元素,那么手动给它添加第二个元素 0
   item[1] = 0;
  }
  this.methods.push(item); // 把符合规定的方法存储到数组存储器中。
 }
}

(2) 为接口辅助类 Interface 定义一个方法 implements,该方法将检测实现类是否符合接口实例的约定。它至少包含两个参数,第1个参数o表示实现类,第2个参数及其后面的参数表示该类将要实现的接口标准。也就是说,可以为一个类指定多个接口约定,这样就可以更灵活的分类设计接口实例。然后遍历第二个及其后面的所有参数,在循环结构中,先洁厕接口是否为接口标准的实例,否则就会抛出异常。再从接口实例的方法存储器中逐一读取方法名,填入类中来验证类的方法是否符合接口实例设置的标准,验证包括方法名、function类型和参数个数。如果有问题,立即抛出异常。

Interface.implements = function(o){ // 用于检测类方法是否符合接口实例的约定 ,此处的o,将来会是类中的this
 if(arguments.length<2){ // 检测该方法传递的数值是否符合规定
  throw new Error("接口约定类应包含至少两个参数。");
 }
 for(var i=1;i<arguments.length; i++){ // 遍历检测类所遵循的实例是否合法
  var interface = arguments[i]; // 这里interface表示接口的实例对象。
  if(interface.constructor !== Interface){
   throw new Error('从第2个以上的参数必须为接口实例');
  }
  for(var j=0;j<interface.methods.length;j++){ // 检测类方法是否符合接口实例的约定
   var method = interface.methods[j][0];
   if(!o[method] || typeof o[method] !== 'function' || o[method].length!==interface.methods[j][1]) {
    throw new Error("该实现类没能履行" + interface.name + "接口方法" + method + "约定");
   }
  }
 }
}

(3) 实例化接口标准,Interface 接口仅仅是个构造函数,也就是个框架协议,还没有制定类应该遵循的具体标准。框架协议中,已经设计好了监测逻辑,一旦实例化接口,并指明类应遵守的约定,那么应用该标准的实例的类就必须准守。下面根据Interface接口标准定义了6个具体的接口实例。

// 设置接口的不同实现实例
var Get = new Interface("Get",[["get",0]]);
var Set = new Interface("Set",[["set",1]]);
var Saying = new Interface("Saying",[["saying",1]]);
var Base = new Interface("Base",[["get",0],["set",1]]);
var Base1 = new Interface("Base1",[["set",1],["get",0]]);
var Base2 = new Interface("Base2",[["get",0],["set",1],["saying",1]]);

(4) 在类中定义应该实现的标准,即类应该遵循的接口约定。

// 创建一个木驴类
function Neddy(){
 this.name = '';
 Interface.implements(this,Base,Get,Set); // 让木驴类实现接口的实例,可以指定多个,也可以只有一个。
}
// 按照接口实例来定义 两个方法get,set
Neddy.prototype.get = function(){
 return this.name;
}
Neddy.prototype.set = function(name){
 this.name = name;
}

(5) 在类中设置了多个接口实例,我们来进行检测

// 检测接口实现
var neddy = new Neddy();
neddy.set("Testing");
console.log(neddy.get()); // Testing

成功完成接口的应用,这里,如果在Neddy类中,我们让它实现的接口实例和 Neddy.prototype中给类定义的方法不统一,或者接口与接口之间有冲突,就会抛出异常。比如我们可以修改Neddy中的接口实现,再给它添加一个接口实例Base2,就会报异常,因为我们没有按照接口的协议,给Neddy添加saying()方法。

上面的举例只是用js来模拟实现接口功能,在实际开发中,我们需要根据不同的需求,开发不同的接口。

扩展知识 :

检测函数的参数列表一般有三种形式

  • ① arguments.callee.length
  • ② 函数名.length
  • ③ arguments.length

其中 ① 和 ② 是一样的,③ 视具体情况而定。
一般函数声明式的定义有个默认属性name继承自Object ,举例如下:

function fn(x,y){
  console.log(fn.name); // fn
  console.log(arguments.callee.name); // fn
  console.log(fn.length); // 2
  console.log(arguments.callee.length); // 2
  console.log(arguments.length); // 视具体情况而定
}
fn(1,2,3); // 分别输出 fn fn 2 2 3

为了说明上面接口检测参数长度 o[method].length  的问题 ,举例如下:

function A(){}
A.prototype.say = function (x,y,z,o) {}
var a = new A();
console.log(a['say'].length); // 4

希望本文所述对大家JavaScript程序设计有所帮助。

Javascript 相关文章推荐
Jquery 插件学习实例1 插件制作说明与tableUI优化
Apr 02 Javascript
JavaScript中的16进制字符(改进)
Nov 21 Javascript
jQuery实现id模糊查询的小例子
Mar 19 Javascript
使用GruntJS构建Web程序之安装篇
Jun 04 Javascript
javascript定义变量时有var和没有var的区别探讨
Jul 21 Javascript
详解javascript遍历方式
Nov 11 Javascript
微信jssdk在iframe页面失效问题的解决措施
Mar 03 Javascript
js中new一个对象的过程
Feb 20 Javascript
基于 Bootstrap Datetimepicker 联动
Aug 03 Javascript
node.js中fs文件系统目录操作与文件信息操作
Feb 24 Javascript
浅谈Node.js 沙箱环境
May 15 Javascript
jQuery阻止事件冒泡实例分析
Jul 03 jQuery
IE11下处理Promise及Vue的单项数据流问题
Jul 24 #Javascript
微信小程序如何引用外部js,外部样式,公共页面模板
Jul 23 #Javascript
详解Vue中的基本语法和常用指令
Jul 23 #Javascript
js如何获取访问IP、地区、当前操作浏览器
Jul 23 #Javascript
node.js express框架简介与实现
Jul 23 #Javascript
js微信分享接口调用详解
Jul 23 #Javascript
详解vue-cli项目开发/生产环境代理实现跨域请求
Jul 23 #Javascript
You might like
谈谈PHP语法(3)
2006/10/09 PHP
php cout&amp;lt;&amp;lt;的一点看法
2010/01/24 PHP
php伪静态之APACHE篇
2014/06/02 PHP
PHP防止post重复提交数据的简单例子
2014/06/07 PHP
Yii2框架dropDownList下拉菜单用法实例分析
2016/07/18 PHP
php常用的工具开发整理
2019/09/26 PHP
JavaScript this调用规则说明
2010/03/08 Javascript
浅析Js中的单引号与双引号问题
2013/11/06 Javascript
jquery实现带二级菜单的导航示例
2014/04/28 Javascript
jquery控制select的text/value值为选中状态
2014/06/03 Javascript
js实现浏览本地文件并显示扩展名的方法
2015/08/17 Javascript
js HTML5 Ajax实现文件上传进度条功能
2016/02/13 Javascript
JavaScript 浏览器兼容性总结及常用浏览器兼容性分析
2016/03/30 Javascript
AngularJS 中文API参考手册
2016/07/28 Javascript
js中scrollTop()方法和scroll()方法用法示例
2016/10/03 Javascript
浅谈js对象的创建和对6种继承模式的理解和遐想
2016/10/16 Javascript
详解JavaScript中this的指向问题
2017/01/20 Javascript
微信小程序开发之toast提示插件使用示例
2017/06/08 Javascript
Javascript实现base64的加密解密方法示例
2017/06/27 Javascript
Vue 使用中的小技巧
2018/04/26 Javascript
vue 下列表侧滑操作实例代码详解
2018/07/24 Javascript
Vue-cli3项目引入Typescript的实现方法
2019/10/18 Javascript
微信小程序弹窗禁止页面滚动的实现代码
2020/12/30 Javascript
Python 专题四 文件基础知识
2017/03/20 Python
解决python文件字符串转列表时遇到空行的问题
2017/07/09 Python
python2.7实现邮件发送功能
2018/12/12 Python
解决pycharm工程启动卡住没反应的问题
2019/01/19 Python
根据tensor的名字获取变量的值方式
2020/01/04 Python
Django 多对多字段的更新和插入数据实例
2020/03/31 Python
pytorch 多分类问题,计算百分比操作
2020/07/09 Python
html5桌面通知(Web Notifications)实例解析
2014/07/07 HTML / CSS
Simons官方网站:加拿大时尚零售商
2020/02/20 全球购物
工作会议通知
2015/04/15 职场文书
幼儿园庆六一主持词
2015/06/30 职场文书
党员发展大会主持词
2015/07/03 职场文书
2016大学生社会实践单位评语
2015/12/01 职场文书