关闭

JavaScript的API设计原则

时间: 2018-01-05阅读: 1194标签: api

前言

本篇文章来自一次公司内部的前端分享,从多个方面讨论了在设计接口时遵循的原则,总共包含了七个大块。系卤煮自己总结的一些经验和教训。本篇博文同时也参考了其他一些文章,相关地址会在后面贴出来。很难做到详尽充实,如果有好的建议或者不对的地方,还望不吝赐教斧正。


一、接口的流畅性

好的接口是流畅易懂的,他主要体现如下几个方面:


1.简单

操作某个元素的css属性,下面是原生的方法:

document.querySelector('#id').style.color = 'red';

封装之后

function a(selector, color) {
   document.querySelector(selector).style.color = color
}
a('#a', 'red');

从几十个字母长长的一行到简简单单的一个函数调用,体现了api设计原则之一:简单易用。


2.可阅读性

a(‘#a’, ‘red’)是个好函数,帮助我们简单实用地改变某个元素,但问题来了,如果第一次使用该函数的人来说会比较困惑,a函数是啥函数,没有人告诉他。开发接口有必要知道一点,大多数人都是懒惰的(包括卤煮自己),从颜色赋值这个函数来说,虽然少写了代码,但是增加了单词字母的个数,使得它不再好记。每次做这件事情的时候都需要有映射关系: a—->color. 如果是简单的几个api倒是无所谓,但是通常一套框架都有几十甚至上百的api,映射成本增加会使得程序员哥哥崩溃。 我们需要的就是使得接口名称有意义,下面我们改写一下a函数:

function letSomeElementChangeColor(selector, color) {
    document.querySelectorAll(selector, color).style.color = color; 
}

letSomeElementChangeColor相对于a来说被赋予了现实语言上的意义,任何人都不需要看说明也能知道它的功能。


3.减少记忆成本

我们刚刚的函数太长了,letSomeElementChangeColor虽然减少了映射成本,有了语言上的意义,但是毫无疑问增加了记忆成本。要知道,包括学霸在内,任何人都不喜欢背单词。不仅仅在此处,原生获取dom的api也同样有这个问题: document.getElementsByClassName; document.getElementsByName; document.querySelectorAll;这些api给人的感觉就是单词太长了,虽然他给出的意义是很清晰,然而这种做法是建立在牺牲简易性和简忆性的基础上进行的。于是我们又再次改写这个之前函数

function setColor(selector, color) {
   //xxxxxxxxxxxx
}

在语言意义不做大的变化前提下,缩减函数名称。使得它易读易记易用。


4.可延伸

所谓延伸就是指函数的使用像流水一样按照书写的顺序执行形成执行链条:

document.getElementById('id').style.color = 'red';
document.getElementById('id').style.fontSize = '12px';
document.getElementById('id').style.backgourdColor = 'pink';

如果我们需要实现像以上有强关联性的业务时,用我们之前的之前的方法是再次封装两个函数 setFontSize, setbackgroundColor; 然后执行它们 setColor(‘id’, ‘red’);setFontSiez(‘id’, ‘12px’); setbackgroundColor(‘id’, ‘pink’); 显然,这样的做法没有懒出境界来;id元素每次都需要重新获取,影响性能,失败;每次都需要添加新的方法,失败; 每次还要调用这些方法,还是失败。下面我们将其改写为可以延伸的函数 首先将获取id方法封装成对象,然后再对象的每个方法中返回这个对象:

function getElement(selector) {
 this.style = document.querySelecotrAll(selector).style;
}
getElement.prototype.color = function(color) {
 this.style.color = color;
 return this;
}
getElement.prototype.background = function(bg) {
 this.style.backgroundColor = bg;
 return this;
}
getElement.prototype.fontSize = function(size) {
 this.style.fontSize = size;
 return this;
}

//调用
var el = new getElement('#id')
el.color('red').background('pink').fontSize('12px');

简单、流畅、易读,它们看起来就像行云流水一样,即在代码性能上得到了提升优化,又在视觉上悦目。后面我们会在参数里面讲到如何继续优化。 
所以,大家都比较喜欢用jquery的api,虽然一个$符号并不代表任何现实意义,但简单的符号有利于我们的使用。它体现了以上的多种原则,简单,易读,易记,链式写法,多参处理。 
Nightmare:

document.getElementById('id').style.color = 'red';
document.getElementById('id').style.fontSize = '12px';
document.getElementById('id').style.backgourdColor = 'pink';

dream:

$('id').css({color:'red', fontSize:'12px', backgroundColor:'pink'})


二、一致性

1.接口的一致性

相关的接口保持一致的风格,一整套 API 如果传递一种熟悉和舒适的感觉,会大大减轻开发者对新工具的适应性。 命名这点事:既要短,又要自描述,最重要的是保持一致性 “在计算机科学界只有两件头疼的事:缓存失效和命名问题” — Phil Karlton 选择一个你喜欢的措辞,然后持续使用。选择一种风格,然后保持这种风格。 
Nightmare:

setColor,
letBackGround
changefontSize
makedisplay

dream:

setColor;
setBackground;
setFontSize
set.........

尽量地保持代码风格和命名风格,使别人读你的代码像是阅读同一个人写的文章一样。


三、参数的处理

1.参数的类型

判断参数的类型为你的程序提供稳定的保障

//我们规定,color接受字符串类型
function setColor(color) {
 if(typeof color !== 'string') return;
 dosomething
}


2.使用json方式传参

使用json的方式传值很多好处,它可以给参数命名,可以忽略参数的具体位置,可以给参数默认值等等 比如下面这种糟糕的情况:

function fn(param1, param2...............paramN)

你必须对应地把每一个参数按照顺序传入,否则你的方法就会偏离你预期去执行,正确的方法是下面的做法。

function fn(json) {
  //为必须的参数设置默认值
 var default = extend({
   param: 'default',
   param1: 'default'
  // ......
 },json)
}

这段函数代码,即便你不传任何参数进来,他也会预期运行。因为在声明的时候,你会根据具体的业务预先决定参数的缺省值。


四、可扩展性

软件设计最重要的原则之一:永远不修改接口,而是去扩展它!可扩展性同时会要求接口的职责单一,多职责的接口很难扩展。 举个栗子:

//需要同时改变某个元素的字体和背景
// Nightmare:
function set(selector, color) {
 document.querySelectroAll(selector).style.color = color;
 document.querySelectroAll(selector).style.backgroundColor = color;
}
//无法扩展改函数,如果需要再次改变字体的大小的话,只能修改此函数,在函数后面填加改变字体大小的代码
//Dream
function set(selector, color) {
 var el = document.querySelectroAll(selector);
 el.style.color = color;
 el.style.backgroundColor = color;
 return el;
}
//需要设置字体、背景颜色和大小
function setAgain (selector, color, px) {
 var el = set(selector, color)
 el.style.fontSize = px;
 return el;
}

以上只是简单的添加颜色,业务复杂而代码又不是你写的时候,你就必须去阅读之前的代码再修改它,显然是不符合开放-封闭原则的。修改后的function是返回了元素对象,使得下次需要改变时再次得到返回值做处理。


2.this的运用

可扩展性还包括对this的以及call和apply方法的灵活运用:

function sayBonjour() {
 alert(this.a)
}
obj.a = 1;
obj.say = sayBonjour;
obj.say();//1
//or
sayBonjour.call||apply(obj);//1


五、对错误的处理

1.预见错误

可以用 类型检测 typeof 或者try…catch。 typeof 会强制检测对象不抛出错误,对于未定义的变量尤其有用。


2.抛出错误

大多数开发者不希望出错了还需要自己去找带对应得代码,最好方式是直接在console中输出,告诉用户发生了什么事情。我们可以用到浏览器为我们提供的api输出这些信息:console.log/warn/error。你还可以为自己的程序留些后路: try…catch。

function error (a) {
 if(typeof a !== 'string') {
   console.error('param a must be type of string')
 }
}
function error() {
 try {
   // some code excucete here maybe throw wrong
 }catch(ex) {
   console.wran(ex);
 }
}


六、可预见性

可预见性味程序接口提供健壮性,为保证你的代码顺利执行,必须为它考虑到非正常预期的情况。我们看下不可以预见的代码和可预见的代码的区别用之前的setColor

//nighware
function set(selector, color) {
 document.getElementById(selector).style.color = color;
}

//dream
zepto.init = function(selector, context) {
 var dom
 // If nothing given, return an empty Zepto collection
 if (!selector) return zepto.Z()
 // Optimize for string selectors
 else if (typeof selector == 'string') {
   selector = selector.trim()
   // If it's a html fragment, create nodes from it
   // Note: In both Chrome 21 and Firefox 15, DOM error 12
   // is thrown if the fragment doesn't begin with <
   if (selector[0] == '<' && fragmentRE.test(selector))
     dom = zepto.fragment(selector, RegExp.$1, context), selector = null
   // If there's a context, create a collection on that context first, and select
   // nodes from there
   else if (context !== undefined) return $(context).find(selector)
   // If it's a CSS selector, use it to select nodes.
   else dom = zepto.qsa(document, selector)
 }
 // If a function is given, call it when the DOM is ready
 else if (isFunction(selector)) return $(document).ready(selector)
 // If a Zepto collection is given, just return it
 else if (zepto.isZ(selector)) return selector
 else {
   // normalize array if an array of nodes is given
   if (isArray(selector)) dom = compact(selector)
   // Wrap DOM nodes.
   else if (isObject(selector))
     dom = [selector], selector = null
   // If it's a html fragment, create nodes from it
   else if (fragmentRE.test(selector))
     dom = zepto.fragment(selector.trim(), RegExp.$1, context), selector = null
   // If there's a context, create a collection on that context first, and select
   // nodes from there
   else if (context !== undefined) return $(context).find(selector)
   // And last but no least, if it's a CSS selector, use it to select nodes.
   else dom = zepto.qsa(document, selector)
 }
 // create a new Zepto collection from the nodes found
 return zepto.Z(dom, selector)
}


以上是zepto的源码,可以看见,作者在预见传入的参数时做了很多的处理。其实可预见性是为程序提供了若干的入口,无非是一些逻辑判断而已。zepto在这里使用了很多的是非判断,这样做的好处当然是代码比之前更健壮,但同时导致了代码的冗长,不适合阅读。总之,可预见性真正需要你做的事多写一些对位置实物的参数。把外部的检测改为内部检测。是的使用的人用起来舒心放心开心。呐!做人嘛最重要的就是海森啦。


七、注释和文档的可读性

一个最好的接口是不需要文档我们也会使用它,但是往往接口量一多和业务增加,接口使用起来也会有些费劲。所以接口文档和注释是需要认真书写的。注释遵循简单扼要地原则,给多年后的自己也给后来者看:

//注释接口,为了演示PPT用
function commentary() {
 //如果你定义一个没有字面意义的变量时,最好为它写上注释:a:没用的变量,可以删除
 var a;
 //在关键和有歧义的地方写上注释,犹如画龙点睛:路由到hash界面后将所有的数据清空结束函数
 return go.Navigate('hash', function(){
   data.clear();
 });
}

原文:http://www.cnblogs.com/constantince/p/5580003.html   

站长推荐

1.云服务推荐: 国内主流云服务商,各类云产品的最新活动,优惠券领取。地址:阿里云腾讯云华为云

2.广告联盟: 整理了目前主流的广告联盟平台,如果你有流量,可以作为参考选择适合你的平台点击进入

链接: http://www.fly63.com/article/detial/326

关闭

你还在用 REST API 吗?

通过 HTTP 发送数据,许多开发人员已经在用 REST 了,而 GraphQL 通常被认为是一种代替遗留 REST API 的技术。本文将对比两者各自的优势、劣势以及它们之间的差异,希望能为你今后项目的技术选型提供帮忙。

什么是RESTful API?

要弄清楚什么是RESTful API,首先要弄清楚什么是REST。REST -- REpresentational State Transfer,英语的直译就是“表现层状态转移”。如果看这个概念,估计没几个人能明白是什么意思。

浏览器中的图像识别 API

在 Native 开发中,Android 和 IOS 平台都在系统层面直接提供给了应用开发识别图像的一些能力,比如对于二维码/条形码的识别,Android 可以使用 barcode API 、 iOS 可以使用 CIQRCodeFeature API 。

webService和Restful

restful是一种架构风格,其核心是面向资源,更简单;而webService底层SOAP协议,主要核心是面向活动;两个都是通过web请求调用接口

API是什么?

API就是接口,就是通道,负责一个程序和其他软件的沟通,本质是预先定义的函数。譬如我们去办事,窗口就类似一个API,如果对于某一件不简单的事情,这个窗口能做到让我们,当然,API不太一样,适用接口隔离原则,即使用多个隔离的接口

使用 JS 来动态操作 css ,你知道几种方法?

JavaScript 可以说是交互之王,它作为脚本语言加上许多 Web Api 进一步扩展了它的特性集,更加丰富界面交互的可操作性。这类 API 的例子包括WebGL API、Canvas API、DOM API,还有一组不太为人所知的 CSS API

JSON Web 令牌(JWT)是如何保护 API 的?

你可以已经听说过 JSON Web Token (JWT) 是目前用于保护 API 的最新技术。与大多数安全主题一样,如果你打算使用它,那很有必要去了解它的工作原理(一定程度上)。问题在于,对 JWT 的大多数解释都是技术性的,这一点让人很头疼。

用Node.js创建安全的 GraphQL API

本文的目标是提供关于如何创建安全的 Node.js GraphQL API 的快速指南。使用 GraphQL API 的目的是什么?什么是GraphQL API?什么是GraphQL查询?GraphQL的好处是什么?GraphQL是否优于REST?为什么我们使用Node.js?

Vue 3.0 体验 Vue Function API

近 Vue 官方公布了 Vue 3.0 最重要的RFC:Function-based component API,并发布了兼容 Vue 2.0 版本的 plugin:vue-function-api,可用于提前体验 Vue 3.0 版本的 Function-based component API

[探索]怎么样的参数能让 JS - API 更灵活

外在决定是否需要了解内在,内在决定是否会一票否决外在。内外结合,好上加好。 开发 API 的时候,把参数的名字和位置确定下来,函数定义就可以说是完成了。

点击更多...

内容以共享、参考、研究为目的,不存在任何商业目的。其版权属原作者所有,如有侵权或违规,请与小编联系!情况属实本人将予以删除!