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 相关文章推荐
javascript 面向对象 function类
May 13 Javascript
Javascript验证上传图片大小[前台处理]
Jul 18 Javascript
javascript中return,return true,return false三者的用法及区别
Nov 17 Javascript
javascript弹出带文字信息的提示框效果
Jul 19 Javascript
angularjs实现下拉列表的选中事件示例
Mar 03 Javascript
[js高手之路]单例模式实现模态框的示例
Sep 01 Javascript
node+express+ejs使用模版引擎做的一个示例demo
Sep 18 Javascript
微信小程序实现MUI数字输入框效果
Jan 31 Javascript
vue将单页面改造成多页面应用的方法
Nov 25 Javascript
微信小程序从注册账号到上架(图文详解)
Jul 17 Javascript
Vue实现简单的留言板
Oct 23 Javascript
JS前端canvas交互实现拖拽旋转及缩放示例
Aug 05 Javascript
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 file_get_contents函数轻松采集html数据
2010/04/22 PHP
php下载文件超时时间的设置方法
2016/10/06 PHP
Docker配置PHP开发环境教程
2016/12/21 PHP
PHP递归实现快速排序的方法示例
2017/12/18 PHP
Laravel框架生命周期与原理分析
2018/06/12 PHP
suggestion开发小结以及对键盘事件的总结(针对中文输入法状态)
2011/12/20 Javascript
js获取class的所有元素
2013/03/28 Javascript
javascript:void(0)是什么意思示例介绍
2013/11/17 Javascript
jQuery照片伸缩效果不影响其他元素的布局
2014/05/09 Javascript
JS实现简单的顶部定时关闭层效果
2014/06/15 Javascript
浅谈javascript 函数属性和方法
2015/01/21 Javascript
详解EasyUi控件中的Datagrid
2017/08/23 Javascript
vue.js中npm安装教程图解
2018/04/10 Javascript
详解vue填坑之解决部分浏览器不支持pushState方法
2018/07/12 Javascript
详解如何写出一个利于扩展的vue路由配置
2019/05/16 Javascript
Echarts动态加载多条折线图的实现代码
2019/05/24 Javascript
解决layer 动态加载select 失效的问题
2019/09/18 Javascript
jquery实现购物车基本功能
2019/10/25 jQuery
vue根据条件不同显示不同按钮的操作
2020/08/04 Javascript
uniapp实现可以左右滑动导航栏
2020/10/21 Javascript
解决Vue keep-alive 调用 $destory() 页面不再被缓存的情况
2020/10/30 Javascript
Python实现的自定义多线程多进程类示例
2018/03/23 Python
python3使用smtplib实现发送邮件功能
2018/05/22 Python
浅谈numpy生成数组的零值问题
2018/11/12 Python
Python八皇后问题解答过程详解
2019/07/29 Python
numpy.array 操作使用简单总结
2019/11/08 Python
浅谈css3中的前缀
2016/07/20 HTML / CSS
Bulk Powders意大利:运动补充在线商店
2019/02/09 全球购物
漫威玩具服装及周边商品官方购物网站:Marvel Shop
2019/05/11 全球购物
请解释流与文件有什么不同
2016/07/29 面试题
仓库文员岗位职责
2014/04/06 职场文书
车辆工程专业求职信
2014/04/28 职场文书
弄虚作假心得体会
2014/09/10 职场文书
2016继续教育培训学习心得体会
2016/01/19 职场文书
详解redis在微服务领域的贡献
2021/10/16 Redis
试用1103暨1103、1101同门大比武 [ DAIWEI ]
2022/04/05 无线电