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

本文实例讲述了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错误与调试技巧总结》、《JavaScript数据结构与算法技巧总结》、《JavaScript遍历算法与技巧总结》及《JavaScript数学运算用法总结》

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

(0)

相关推荐

  • JavaScript接口实现代码 (Interfaces In JavaScript)

    在实际中,我们可以在注释中定义好接口,在实际的代码中予以实现 比如: 复制代码 代码如下: /* interface Composite { function add(child); function remove(child); function getChild(index); } interface FormItem { function save(); } */ var CompositeForm = function(id, method, action) { // implement

  • JavaScript通过filereader接口读取文件

    使用FileReader接口的readAsDataURL方法实现图片的预览. 源代码: <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=gb2312" /> <title>通过filereader接口读取文件</title> <script type="text/javascript"

  • 学习JavaScript设计模式(接口)

    1.接口概述 1).什么是接口? 接口是提供了一种用以说明一个对象应该具有哪些方法的手段.尽管它可以表明这些方法的语义,但它并不规定这些方法应该如何实现. 2). 接口之利 促进代码的重用. 接口可以告诉程序员一个类实现了哪些方法,从而帮助其使用这个类. 有助于稳定不同类之前的通信方式. 测试和调式因此也能变得更轻松. 在javascript这种弱类型语言中,类型不匹配错误很难跟踪.使用接口可以让这种错误的查找变午更容易一点,因为此时如果一个对象不像所要求的类型,或者没有实现必要的方法,那么你会

  • 深入理解JavaScript系列(21):S.O.L.I.D五大原则之接口隔离原则ISP详解

    前言 本章我们要讲解的是S.O.L.I.D五大原则JavaScript语言实现的第4篇,接口隔离原则ISP(The Interface Segregation Principle). 英文原文:http://freshbrewedcode.com/derekgreer/2012/01/08/solid-javascript-the-interface-segregation-principle/ 注:这篇文章作者写得比较绕口,所以大叔理解得也比较郁闷,凑合着看吧,别深陷进去了 接口隔离原则的描述

  • Javascript 面向对象(三)接口代码

    程序中的接口:规定好有几个方法,方法名是什么.(因程序中要完成任务,都是通过函数或者说方法去实现的.) javascript中的接口:把实例出来的某"类型对象".和实例出来的"接口对象",进行一个比较,符合规则,就可以说:这个对象实现了规定的接口: (接口类:通过这个类,来实例出不同的接口.即不同的接口实例,也就是不同的方法个数和方法名称) (进行比较:其实质就是判断子类型实例出来的对象,是否有接口对象中所保存的方法名,还有个数.) 小实例: 电话类,下面可以有&q

  • 面向对象的Javascript之二(接口实现介绍)

    就足以说明接口在面向对象的领域中有多重要.但JS却不像其他面向对象的高级语言(C#,Java,C++等)拥有内建的接口机制,以确定一组对象和另一组对象包含相似的的特性.所幸的是JS拥有强大的灵活性(我在上文已谈过),这使得模仿接口特性又变得非常简单.那么到底是接口呢? 接口,为一些具有相似行为的类之间(可能为同一种类型,也可能为不同类型)提供统一的方法定义,使这些类之间能够很好的实现通信. 那使用接口到底有哪些好处呢?简单地说,可提高系统相似模块的重用性,使得不同类的通信更加稳固.一旦实现接口,

  • Javascript之面向对象--接口

    接口:对实现某一种任务.功能,目的等,所规定的一些原则. 程序中的接口:规定好有几个方法,方法名是什么.(因程序中要完成任务,都是通过函数或者说方法去实现的.) javascript中的接口:把实例出来的某"类型对象".和实例出来的"接口对象",进行一个比较,符合规则,就可以说:这个对象实现了规定的接口: (接口类:通过这个类,来实例出不同的接口.即不同的接口实例,也就是不同的方法个数和方法名称) (进行比较:其实质就是判断子类型实例出来的对象,是否有接口对象中所保

  • JavaScript fetch接口案例解析

    在 AJAX 时代,进行 API 等网络请求都是通过 XMLHttpRequest 或者封装后的框架进行网络请求. 现在产生的 fetch 框架简直就是为了提供更加强大.高效的网络请求而生,虽然在目前会有一点浏览器兼容的问题,但是当我们进行 Hybrid App 开发的时候,如我之前介绍的 Ionic 和 React Native,都可以使用 fetch 进行完美的网络请求. 如果看网上的fetch教程,会首先对比XMLHttpRequest和fetch的优劣,然后引出一堆看了很快会忘记的内容(

  • JavaScript接口的实现三种方式(推荐)

    Javascript模仿接口可以有三种方式:1.注释法 2.检查属性法 3.鸭式辨形法 1.注释法:此方法属于程序文档范畴,对接口的继承实现完全依靠程序员自觉 /* interface People{ function createHead(); function createBody(); } */ var woman = function(name){ //implements People interface this.name = name; } woman.prototype.show

  • js创建数据共享接口——简化框架之间相互传值

    很多框架存在父子关系,操作起来十分麻烦,很多同学经常出现这样悲催的代码: 复制代码 代码如下: window.parent.document.getElementById("main") .contentWindow.document.getElementById('input').value = document.getElementById('myIframe') .contentWindow.document.getElementById('s0').value; 其实这个问题可

随机推荐