深入浅析JavaScript的API设计原则


Posted in Javascript onJune 14, 2016

一、接口的流畅性

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

1.简单

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

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

封装之后

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

从几十个字母长长的一行到简简单单的一个函数调用,体现了api简单易用

2.可阅读性

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

function letSomeElementChangeColor(selector, color) {
document.querySelectorAll(selector, 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 = 'px';
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 = color;
return this;
}
getElement.prototype.fontSize = function(size) {
this.style.fontSize = size;
return this;
}
//调用
var el = new getElement('#id')
el.color('red').background('pink').fontSize('px');

简单、流畅、易读后面我们会在参数里面讲到如何继续优化。所以,大家都比较喜欢用jquery的api,虽然一个$符号并不代表任何现实意义,但简单的符号有利于我们的使用。它体现了以上的多种原则,简单,易读,易记,链式写法,多参处理。

nightware:

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

dream:

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

二、一致性

1.接口的一致性

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

Nightware:

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',
param: 'default'
......
},json)
}

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

四、可扩展性

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

//需要同时改变某个元素的字体和背景 
// Nightware:
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 = ;
obj.say = sayBonjour;
obj.say();//
//or
sayBonjour.call||apply(obj);//

五、对错误的处理

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 and Firefox , DOM error 
// is thrown if the fragment doesn't begin with <
if (selector[] == '<' && fragmentRE.test(selector))
dom = zepto.fragment(selector, RegExp.$, 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.$, 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();
});
}

最后

推荐markdown语法书写API文档,github御用文档编写语法。简单、快速,代码高亮、话不多说上图

深入浅析JavaScript的API设计原则

以上所述是小编给大家介绍的JavaScript的API设计原则的全部叙述,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对三水点靠木网站的支持!

Javascript 相关文章推荐
JavaScript 函数参数是传值(byVal)还是传址(byRef) 分享
Jul 02 Javascript
20个实用的JavaScript技巧分享
Nov 28 Javascript
javascript定义变量时加var与不加var的区别
Dec 22 Javascript
分享自己用JS做的扫雷小游戏
Feb 17 Javascript
Vue.js教程之axios与网络传输的学习实践
Apr 29 Javascript
JavaScript 异步调用
Oct 25 Javascript
JQuery Ajax动态加载Table数据的实例讲解
Aug 09 jQuery
快速解决Vue项目在IE浏览器中显示空白的问题
Sep 04 Javascript
优雅的处理vue项目异常实战记录
Jun 05 Javascript
Vue-cli 移动端布局和动画使用详解
Aug 10 Javascript
vue el-upload上传文件的示例代码
Dec 21 Vue.js
vue使用lodop打印控件实现浏览器兼容打印的方法
Feb 07 Vue.js
jQuery.Callbacks()回调函数队列用法详解
Jun 14 #Javascript
基于gulp合并压缩Seajs模块的方式说明
Jun 14 #Javascript
JS去除空格和换行的正则表达式(推荐)
Jun 14 #Javascript
javascript用正则表达式过滤空格的实现代码
Jun 14 #Javascript
三种带箭头提示框总结实例
Jun 14 #Javascript
js判断输入字符串是否为空、空格、null的方法总结
Jun 14 #Javascript
简单实现的JQuery文本框水印插件
Jun 14 #Javascript
You might like
现磨咖啡骗局!现磨咖啡=新鲜咖啡?现磨咖啡背后的猫腻你不懂!
2019/03/28 冲泡冲煮
php一维二维数组键排序方法实例总结
2014/11/13 PHP
PHP实现仿百度文库,豆丁在线文档效果(word,excel,ppt转flash)
2016/03/10 PHP
ThinkPHP发送邮件示例代码
2016/10/08 PHP
JS getStyle获取最终样式函数代码
2010/04/01 Javascript
JS图片浏览组件PhotoLook的公开属性方法介绍和进阶实例代码
2010/11/09 Javascript
JQUERY 实现窗口滚动搜索框停靠效果(类似滚动停靠)
2013/03/27 Javascript
js判断选择的时间是否大于今天的代码
2013/08/20 Javascript
js如何设置在iframe框架中指定div不显示
2013/12/04 Javascript
JS实现的仿东京商城菜单、仿Win右键菜单及仿淘宝TAB特效合集
2015/09/28 Javascript
学习使用jquery iScroll.js移动端滚动条插件
2020/03/24 Javascript
理解javascript中的Function.prototype.bind的方法
2017/02/03 Javascript
React组件refs的使用详解
2018/02/09 Javascript
vue2 全局变量的设置方法
2018/03/09 Javascript
AngularJS与BootStrap模仿百度分页的示例代码
2018/05/23 Javascript
Vue项目中跨域问题解决方案
2018/06/05 Javascript
Vuex的初探与实战小结
2018/11/26 Javascript
在vue项目中使用sass语法问题
2019/07/18 Javascript
JS实现“全选”和&quot;全不选&quot;功能代码实例
2020/02/06 Javascript
原生js实现碰撞检测
2020/03/12 Javascript
在Vue.js中使用TypeScript的方法
2020/03/19 Javascript
python中使用xlrd读excel使用xlwt写excel的实例代码
2018/01/31 Python
Sanic框架路由用法实例分析
2018/07/16 Python
TensorFlow卷积神经网络之使用训练好的模型识别猫狗图片
2019/03/14 Python
可视化pytorch 模型中不同BN层的running mean曲线实例
2020/06/24 Python
python 写一个性能测试工具(一)
2020/10/24 Python
Python tkinter之Bind(绑定事件)的使用示例
2021/02/05 Python
html5标记文字_动力节点Java学院整理
2017/07/11 HTML / CSS
canvas仿写贝塞尔曲线的示例代码
2017/12/29 HTML / CSS
施华洛世奇意大利官网:SWAROVSKI意大利
2018/07/23 全球购物
年度考核自我鉴定
2013/11/09 职场文书
商务英语专业毕业生求职信
2014/07/06 职场文书
文明单位汇报材料
2014/12/24 职场文书
六种css3实现的边框过渡效果
2021/04/22 HTML / CSS
详解Python描述符的工作原理
2021/06/11 Python
详解CSS中的特指度和层叠问题
2021/07/15 HTML / CSS