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 相关文章推荐
HTML node相关的一些资料整理
Jan 01 Javascript
扩展javascript的Date方法实现代码(prototype)
Nov 20 Javascript
JQuery 应用 JQuery.groupTable.js
Dec 15 Javascript
基于Jquery实现键盘按键监听
May 11 Javascript
Bootstrap的Refresh Icon也spin起来
Jul 13 Javascript
Bootstrap中data-target 到底是什么
Feb 14 Javascript
JavaScript生成图形验证码
Aug 24 Javascript
vue watch普通监听和深度监听实例详解(数组和对象)
Aug 16 Javascript
利用百度echarts实现图表功能简单入门示例【附源码下载】
Jun 10 Javascript
Vue-cli3.x + axios 跨域方案踩坑指北
Jul 04 Javascript
Vue的click事件防抖和节流处理详解
Nov 13 Javascript
详解vue-router的导航钩子(导航守卫)
Nov 02 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静态新闻列表自动生成代码
2007/06/14 PHP
php 运行效率总结(提示程序速度)
2009/11/26 PHP
PHP 设置MySQL连接字符集的方法
2011/01/02 PHP
php中的常用魔术方法总结
2013/08/02 PHP
ThinkPHP使用PHPExcel实现Excel数据导入导出完整实例
2014/07/22 PHP
浅谈PHP拦截器之__set()与__get()的理解与使用方法
2016/10/18 PHP
PHP实现基于回溯法求解迷宫问题的方法详解
2017/08/17 PHP
Laravel中Facade的加载过程与原理详解
2017/09/22 PHP
php中各种定义变量的方法小结
2017/10/18 PHP
php设计模式之原型模式分析【星际争霸游戏案例】
2020/03/23 PHP
JavaScript输入邮箱自动提示实例代码
2014/01/13 Javascript
JavaScript的Backbone.js框架的一些使用建议整理
2016/02/14 Javascript
JavaScript原生对象常用方法总结(推荐)
2016/05/13 Javascript
Vue.js每天必学之过渡与动画
2016/09/06 Javascript
Angular2表单自定义验证器的实现
2016/10/19 Javascript
vue.js过滤器+ajax实现事件监听及后台php数据交互实例
2018/05/22 Javascript
JavaScript基础教程之如何实现一个简单的promise
2018/09/11 Javascript
apicloud拉起小程序并传递参数的方法示例
2018/11/21 Javascript
node.js微信小程序配置消息推送的实现
2019/02/13 Javascript
vue实现的请求服务器端API接口示例
2019/05/25 Javascript
解决vue.js提交数组时出现数组下标的问题
2019/11/05 Javascript
详解nginx配置vue h5 history去除#号
2020/11/09 Javascript
编写v-for循环的技巧汇总
2020/12/01 Javascript
python进阶教程之动态类型详解
2014/08/30 Python
Python中3种内建数据结构:列表、元组和字典
2014/11/30 Python
用Python编写一个简单的Lisp解释器的教程
2015/04/03 Python
利用python获取当前日期前后N天或N月日期的方法示例
2017/07/30 Python
Python比较2个时间大小的实现方法
2018/04/10 Python
解决pycharm回车之后不能换行或不能缩进的问题
2019/01/16 Python
python 利用百度API识别图片文字(多线程版)
2020/12/14 Python
销售总监工作职责
2013/11/21 职场文书
自我鉴定书
2014/03/24 职场文书
学习雷锋精神演讲稿
2014/05/10 职场文书
迎新生晚会主持词
2015/06/30 职场文书
总经理年会致辞
2015/07/29 职场文书
vue实现锚点定位功能
2021/06/29 Vue.js