目录
一、什么是接口
二、接口的利与弊
三、在JavaScript中模仿接口
四、Interface类
五、依赖于接口的设计模式
首页 web前端 js教程 浅谈JavaScript中的接口代码详解

浅谈JavaScript中的接口代码详解

Mar 04, 2017 pm 03:16 PM
javascript

一、什么是接口

接口是面向对象JavaScript程序员的工具箱中最有用的工具之一。在设计模式中提出的可重用的面向对象设计的原则之一就是“针对接口编程而不是实现编程”,即我们所说的面向接口编程,这个概念的重要性可见一斑。但问题在于,在JavaScript的世界中,没有内置的创建或实现接口的方法,也没有可以判断一个对象是否实现了与另一个对象相同的一套方法,这使得对象之间很难互换使用,好在JavaScript拥有出色的灵活性,这使得模拟传统面向对象的接口,添加这些特性并非难事。接口提供了一种用以说明一个对象应该具有哪些方法的手段,尽管它可以表明这些方法的含义,但是却不包含具体实现。有了这个工具,就能按对象提供的特性对它们进行分组。例如,假如A和B以及接口I,即便A对象和B对象有极大的差异,只要他们都实现了I接口,那么在A.I(B)方法中就可以互换使用A和B,如B.I(A)。还可以使用接口开发不同的类的共同性。如果把原本要求以一个特定的类为参数的函数改为要求以一个特定的接口为参数的函数,那么所有实现了该接口的对象都可以作为参数传递给它,这样一来,彼此不相关的对象也可以被相同地对待。

二、接口的利与弊

既定的接口具有自我描述性,并能够促进代码的重用性,接口可以提供一种信息,告诉外部一个类需要实现哪些方法。还有助于稳定不同类之间的通信方式,减少了继承两个对象的过程中出现的问题。这对于调试也是有帮助的,在JavaScript这种弱类型语言中,类型不匹配很难追踪,使用接口时,如果出现了问题,会有更明确的错误提示信息。当然接口并非完全没有缺点,如果大量使用接口会一定程度上弱化其作为弱类型语言的灵活性,另一方面,JavaScript并没有对接口的内置的支持,只是对传统的面向对象的接口进行模拟,这会使本身较为灵活的JavaScript变得更加难以驾驭。此外,任何实现接口的方式都会对性能造成影响,某种程度上归咎于额外的方法调用开销。接口使用的最大的问题在于,JavaScript不像是其他的强类型语言,如果不遵守接口的约定,就会编译失败,其灵活性可以有效地避开上述问题,如果是在协同开发的环境下,其接口很有可能被破坏而不会产生任何错误,也就是不可控性。

在面向对象的语言中,使用接口的方式大体相似。接口中包含的信息说明了类需要实现的方法以及这些方法的签名。类的定义必须明确地声明它们实现了这些接口,否则是不会编译通过的。显然在JavaScript中我们不能如法炮制,因为不存在interface和implement关键字,也不会在运行时对接口是否遵循约定进行检查,但是我们可以通过辅助方法和显式地检查模仿出其大部分特性。

三、在JavaScript中模仿接口

在JavaScript中模仿接口主要有三种方式:通过注释、属性检查和鸭式辩型法,以上三种方式有效结合,就会产生类似接口的效果。

注释是一种比较直观地把与接口相关的关键字(如interface、implement等)与JavaScript代码一同放在注释中来模拟接口,这是最简单的方法,但是效果最差。代码如下:

//以注释的形式模仿描述接口
/*
interface Composite{
    function add(child);
    function remove(child);
    function getName(index);
}

interface FormItem{
    function save();
}
*/

//以注释的形式模仿使用接口关键字
var CompositeForm =function(id , method,action) { //implements Composite , FormItem
    // do something
}
//模拟实现具体的接口方法 此处实现Composite接口
CompositeForm.prototype.Add=function(){
    // do something
}

CompositeForm.prototype.remove=function(){
    // do something
}

CompositeForm.prototype.getName=function(){
    // do something
}

//模拟实现具体的接口方法 此处实现FormItem接口
Composite.prototype.save=function(){
    // do something
}
登录后复制

这种方式其实并不是很好,因为这种模仿还只停留在文档规范的范畴,开发人员是否会严格遵守该约定有待考量,对接口的遵守完全依靠开发人员的自觉性。另外,这种方式并不会去检查某个函数是否真正地实现了我们约定的“接口”。尽管如此,这种方式也有优点,它易于实现而不需要额外的类或者函数,可以提高代码的可重用性,因为类实现的接口都有注释说明。这种方式不会影响到文件占用的空间或执行速度,因为注释的代码可以在部署的时候轻松剔除。但是由于不会提供错误消息,它对测试和调试没什么帮助。下面的一种方式会对是否实现接口进行检查,代码如下:

//以注释的形式模仿使用接口关键字
var CompositeForm =function(id , method,action) { //implements Composite , FormItem
    // do something
    this.implementsinterfaces=['Composite','FormItem']; //显式地把接口放在implementsinterfaces中
}

//检查接口是否实现
function implements(Object){
    for(var i=0 ;i< arguments.length;i++){
        var interfaceName=arguments[i];
        var interfaceFound=false;
        for(var j=0;j<Object.implementsinterfaces.length;j++){
            if(Object.implementsinterfaces[j]==interfaceName){
                interfaceFound=true;
                break;
            }
        }
        if(!interfaceFound){
            return false;
        }else{
            return true;
        }
    }
}

function AddForm(formInstance){
    if(!implements(formInstance,&#39;Composite&#39;,&#39;FormItem&#39;)){ 
        throw new Error(&#39;Object does not implements required interface!&#39;);
    }
}
登录后复制

上述代码是在方式一的基础上进行完善,在这个例子中,CompositeForm宣称自己实现了Composite和FormItem这两个接口,其做法是把这两个接口的名称加入一个implementsinterfaces的数组。显式地声明自己支持什么接口。任何一个要求其参数属性为特定类型的函数都可以对这个属性进行检查,并在所需要的接口未在声明之中时抛出错误。这种方式相对于上一种方式,多了一个强制性的类型检查。但是这种方法的缺点在于它并未保证类真正地实现了自称实现的接口,只是知道它声明自己实现了这些接口。其实类是否声明自己支持哪些接口并不重要,只要它具有这些接口中的方法就行。鸭式辩型(像鸭子一样走路并且嘎嘎叫的就是鸭子)正是基于这样的认识,它把对象实现的方法集作为判断它是不是某个类的实例的唯一标准。这种技术在检查一个类是否实现了某个接口时也可以大显身手。这种方法的背后观点很简单:如果对象具有与接口定义的方法同名的所有方法,那么就可以认为它实现了这个接口。可以使用一个辅助函数来确保对象具有所有必需的方法,代码如下:

//interface
var Composite =new Interface(&#39;Composite&#39;,[&#39;add&#39;,&#39;remove&#39;,&#39;getName&#39;]);
var FormItem=new Interface(&#39;FormItem&#39;,[&#39;save&#39;]);

//class
var Composite=function(id,method,action){

}

//Common Method
function AddForm(formInstance){
    ensureImplements(formInstance,Composite,FormItem);
    //如果该函数没有实现指定的接口,这个函数将会报错
}
登录后复制

与另外两种方式不同,这种方式无需注释,其余的各个方面都是可以强制实施的。EnsureImplements函数需要至少两个参数。第一个参数是想要检查的对象,其余的参数是被检查对象的接口。该函数检查器第一个参数代表的对象是否实现了那些接口所声明的方法,如果漏掉了任何一个,就会抛错,其中会包含被遗漏的方法的有效信息。这种方式不具备自我描述性,需要一个辅助类和辅助函数来帮助实现接口检查,而且它只关心方法名称,并不检查参数的名称、数目或类型。

四、Interface类

在下面的代码中,对Interface类的所有方法的参数都进行了严格的控制,如果参数没有验证通过,那么就会抛出异常。加入这种检查的目的就是,如果在执行过程中没有抛出异常,那么就可以肯定接口得到了正确的声明和实现。

var Interface = function(name ,methods){
    if(arguments.length!=2){
        throw new Error(&#39;2 arguments required!&#39;);
    }
    this.name=name;
    this.methods=[];
    for(var i=0;len=methods.length;i<len;i++){
        if(typeof(methods[i]!==&#39;String&#39;)){
            throw new Error(&#39;method name must be String!&#39;);
        }
        this.methods.push(methods[i]);
    }
}

Interface.ensureImplements=function(object){
    if(arguments.length<2){
        throw new Error(&#39;2 arguments required at least!&#39;);
    }
    for(var i=0;len=arguments.length;i<len;i++){
        var interface=arguments[i];
        if(interface.constructor!==Interface){
            throw new Error(&#39;instance must be Interface!&#39;);
        }
        for(var j=0;methodLength=interface.methods.length;j<methodLength;j++){
            var method=interface.methods[j];
            if(!object[method]||typeof(object[method])==&#39;function&#39;)){
                throw new Error(&#39;object does not implements method!&#39;);
            }    
        }
    }
}
登录后复制

其实多数情况下,接口并不是经常被使用的,严格的类型检查并不总是明智的。但是在设计复杂的系统的时候,接口的作用就体现出来了,这看似降低了灵活性,却同时也降低了耦合性,提高了代码的重用性。这在大型系统中是比较有优势的。在下面的例子中,声明了一个displayRoute方法,要求其参数具有三个特定的方法,通过Interface对象和ensureImplements方法来保证这三个方法的实现,否则将会抛出错误。

//声明一个接口,描述该接口包含的方法
 var DynamicMap=new Interface{&#39;DynamicMap&#39;,[&#39;centerOnPoint&#39;,&#39;zoom&#39;,&#39;draw&#39;]};

 //声明一个displayRoute方法
 function displayRoute(mapInstance){
    //检验该方法的map
    //检验该方法的mapInsstance是否实现了DynamicMap接口,如果未实现则会抛出
    Interface.ensureImplements(mapInstance,DynamicMap);
    //如果实现了则正常执行
    mapInstance.centerOnPoint(12,22);
    mapInstance.zoom(5);
    mapInstance.draw();
 }
登录后复制

下面的例子会将一些数据以网页的形式展现出来,这个类的构造器以一个TestResult的实例作为参数。该类会对TestResult对象所包含的数据进行格式化(Format)后输出,代码如下:

var ResultFormatter=function(resultObject){
     //对resultObject进行检查,保证是TestResult的实例
     if(!(resultObject instanceof TestResult)){
         throw new Error(&#39;arguments error!&#39;);
     }
     this.resultObject=resultObject;
 }

 ResultFormatter.prototype.renderResult=function(){
     var dateOfTest=this.resultObject.getData();
     var resultArray=this.resultObject.getResults();
     var resultContainer=document.createElement(&#39;p&#39;);
     var resultHeader=document.createElement(&#39;h3&#39;);
     resultHeader.innerHTML=&#39;Test Result from &#39;+dateOfTest.toUTCString();
     resultContainer.appendChild(resultHeader);

     var resultList=document.createElement(&#39;ul&#39;);
     resultContainer.appendChild(resultList);

     for(var i=0;len=resultArray.length;i<len;i++){
         var listItem=document.createElement(&#39;li&#39;);
         listItem.innerHTML=resultArray[i];
         resultList.appendChild(&#39;listItem&#39;);
     }
     return resultContainer;
 }
登录后复制

该类的构造器会对参数进行检查,以确保其的确为TestResult的类的实例。如果参数达不到要求,构造器将会抛出一个错误。有了这样的保证,在编写renderResult方法的时候,就可以认定有getData和getResult两个方法。但是,构造函数中,只对参数的类型进行了检查,实际上这并不能保证所需要的方法都得到了实现。TestResult类会被修改,致使其失去这两个方法,但是构造器中的检查依旧会通过,只是renderResult方法不再有效。

此外,构造器中的这个检查施加了一些不必要的限制。它不允许使用其他的类的实例作为参数,否则会直接抛错,但是问题来了,如果有另一个类也包含并实现了getData和getResult方法,它本来可以被ResultFormatter使用,却因为这个限制而无用武之地。

解决问题的办法就是删除构造器中的校验,并使用接口代替。我们采用这个方案对代码进行优化:

//接口的声明
var resultSet =new Interface(&#39;ResultSet&#39;,[&#39;getData&#39;,&#39;getResult&#39;]);

//修改后的方案
 var ResultFormatter =function(resultObject){
     Interface.ensureImplements(resultObject,resultSet);
     this.resultObject=resultObject;
 }
登录后复制

上述代码中,renderResult方法保持不变,而构造器却采用的ensureImplements方法,而不是typeof运算符。现在的这个构造器可以接受任何符合接口的类的实例了。

五、依赖于接口的设计模式

<1>工厂模式:对象工厂所创建的具体对象会因具体情况而不同。使用接口可以确保所创建的这些对象可以互换使用,也就是说对象工厂可以保证其生产出来的对象都实现了必需的方法;

<2>组合模式:如果不使用接口就不可能使用这个模式,其中心思想是可以将对象群体与其组成对象同等对待。这是通过接口来做到的。如果不进行鸭式辩型或类型检查,那么组合模式就会失去大部分意义;

<3>装饰者模式:装饰者通过透明地为另一个对象提供包装而发挥作用。这是通过实现与另外那个对象完全一致的接口实现的。对于外界而言,一个装饰者和它所包装的对象看不出有什么区别,所以使用Interface来确保所创建的装饰者实现了必需的方法;

<4>命令模式:代码中所有的命令对象都有实现同一批方法(如run、ecxute、do等)通过使用接口,未执行这些命令对象而创建的类可以不必知道这些对象具体是什么,只要知道他们都正确地实现了接口即可。借此可以创建出模块化程度很高的、耦合度很低的API。

 以上就是浅谈JavaScript中的接口代码详解的内容,更多相关内容请关注PHP中文网(www.php.cn)!

本站声明
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn

热AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智能驱动的应用程序,用于创建逼真的裸体照片

AI Clothes Remover

AI Clothes Remover

用于从照片中去除衣服的在线人工智能工具。

Undress AI Tool

Undress AI Tool

免费脱衣服图片

Clothoff.io

Clothoff.io

AI脱衣机

AI Hentai Generator

AI Hentai Generator

免费生成ai无尽的。

热门文章

R.E.P.O.能量晶体解释及其做什么(黄色晶体)
3 周前 By 尊渡假赌尊渡假赌尊渡假赌
R.E.P.O.最佳图形设置
3 周前 By 尊渡假赌尊渡假赌尊渡假赌
R.E.P.O.如果您听不到任何人,如何修复音频
3 周前 By 尊渡假赌尊渡假赌尊渡假赌
WWE 2K25:如何解锁Myrise中的所有内容
4 周前 By 尊渡假赌尊渡假赌尊渡假赌

热工具

记事本++7.3.1

记事本++7.3.1

好用且免费的代码编辑器

SublimeText3汉化版

SublimeText3汉化版

中文版,非常好用

禅工作室 13.0.1

禅工作室 13.0.1

功能强大的PHP集成开发环境

Dreamweaver CS6

Dreamweaver CS6

视觉化网页开发工具

SublimeText3 Mac版

SublimeText3 Mac版

神级代码编辑软件(SublimeText3)

如何使用WebSocket和JavaScript实现在线语音识别系统 如何使用WebSocket和JavaScript实现在线语音识别系统 Dec 17, 2023 pm 02:54 PM

如何使用WebSocket和JavaScript实现在线语音识别系统引言:随着科技的不断发展,语音识别技术已经成为了人工智能领域的重要组成部分。而基于WebSocket和JavaScript实现的在线语音识别系统,具备了低延迟、实时性和跨平台的特点,成为了一种被广泛应用的解决方案。本文将介绍如何使用WebSocket和JavaScript来实现在线语音识别系

WebSocket与JavaScript:实现实时监控系统的关键技术 WebSocket与JavaScript:实现实时监控系统的关键技术 Dec 17, 2023 pm 05:30 PM

WebSocket与JavaScript:实现实时监控系统的关键技术引言:随着互联网技术的快速发展,实时监控系统在各个领域中得到了广泛的应用。而实现实时监控的关键技术之一就是WebSocket与JavaScript的结合使用。本文将介绍WebSocket与JavaScript在实时监控系统中的应用,并给出代码示例,详细解释其实现原理。一、WebSocket技

如何利用JavaScript和WebSocket实现实时在线点餐系统 如何利用JavaScript和WebSocket实现实时在线点餐系统 Dec 17, 2023 pm 12:09 PM

如何利用JavaScript和WebSocket实现实时在线点餐系统介绍:随着互联网的普及和技术的进步,越来越多的餐厅开始提供在线点餐服务。为了实现实时在线点餐系统,我们可以利用JavaScript和WebSocket技术。WebSocket是一种基于TCP协议的全双工通信协议,可以实现客户端与服务器的实时双向通信。在实时在线点餐系统中,当用户选择菜品并下单

如何使用WebSocket和JavaScript实现在线预约系统 如何使用WebSocket和JavaScript实现在线预约系统 Dec 17, 2023 am 09:39 AM

如何使用WebSocket和JavaScript实现在线预约系统在当今数字化的时代,越来越多的业务和服务都需要提供在线预约功能。而实现一个高效、实时的在线预约系统是至关重要的。本文将介绍如何使用WebSocket和JavaScript来实现一个在线预约系统,并提供具体的代码示例。一、什么是WebSocketWebSocket是一种在单个TCP连接上进行全双工

JavaScript和WebSocket:打造高效的实时天气预报系统 JavaScript和WebSocket:打造高效的实时天气预报系统 Dec 17, 2023 pm 05:13 PM

JavaScript和WebSocket:打造高效的实时天气预报系统引言:如今,天气预报的准确性对于日常生活以及决策制定具有重要意义。随着技术的发展,我们可以通过实时获取天气数据来提供更准确可靠的天气预报。在本文中,我们将学习如何使用JavaScript和WebSocket技术,来构建一个高效的实时天气预报系统。本文将通过具体的代码示例来展示实现的过程。We

简易JavaScript教程:获取HTTP状态码的方法 简易JavaScript教程:获取HTTP状态码的方法 Jan 05, 2024 pm 06:08 PM

JavaScript教程:如何获取HTTP状态码,需要具体代码示例前言:在Web开发中,经常会涉及到与服务器进行数据交互的场景。在与服务器进行通信时,我们经常需要获取返回的HTTP状态码来判断操作是否成功,根据不同的状态码来进行相应的处理。本篇文章将教你如何使用JavaScript获取HTTP状态码,并提供一些实用的代码示例。使用XMLHttpRequest

javascript中如何使用insertBefore javascript中如何使用insertBefore Nov 24, 2023 am 11:56 AM

用法:在JavaScript中,insertBefore()方法用于在DOM树中插入一个新的节点。这个方法需要两个参数:要插入的新节点和参考节点(即新节点将要被插入的位置的节点)。

如何在JavaScript中获取HTTP状态码的简单方法 如何在JavaScript中获取HTTP状态码的简单方法 Jan 05, 2024 pm 01:37 PM

JavaScript中的HTTP状态码获取方法简介:在进行前端开发中,我们常常需要处理与后端接口的交互,而HTTP状态码就是其中非常重要的一部分。了解和获取HTTP状态码有助于我们更好地处理接口返回的数据。本文将介绍使用JavaScript获取HTTP状态码的方法,并提供具体代码示例。一、什么是HTTP状态码HTTP状态码是指当浏览器向服务器发起请求时,服务

See all articles