JavaScript的API设计原则
前言
本篇博文来自一次公司内部的前端分享,从多个方面讨论了在设计接口时的原则,总共包含了七个大块。系卤煮自己总结的一些经验教训。同时也参考了一些文章,地址会在后面贴出来。很难做到详尽充实,如果有好的建议或者不对的地方,还望不吝赐教斧正。
一、接口的流畅性
好的接口是流畅易懂的,他主要体现如下几个方面:
1.简单
操作某个元素的css属性,下面是原生的方法:
- document.querySelectorAll('#id').style.color = 'red';
封装之后
- function a(selector, color) {
- document.querySelectorAll(selector)[0].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 = '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 = 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('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 选择一个你喜欢的措辞,然后持续使用。选择一种风格,然后保持这种风格。
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',
- 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();
- });
- }
最后
推荐markdown语法书写API文档,github御用文档编写语法。简单、快速,代码高亮、话不多说上图
作者:佚名
来源:51CTO
JavaScript的API设计原则相关推荐
- API设计原则 - Qt官网的设计实践总结
原文链接:API Design Principles - Qt Wiki 基于Gary的影响力上 Gary Gao 的译文稿:C++的API设计指导 译文发在酷壳 - CoolShell:API设计原 ...
- API设计原则(觉得太合适,转发做记录)
API设计原则 对于云计算系统,系统API实际上处于系统设计的统领地位,正如本文前面所说,K8s集 群系统每支持一项新功能,引入一项新技术,一定会新引入对应的API对象,支持对该 功能的管理操作,理解 ...
- javascript规范以及设计原则
UI layer的分离 针对CSS: 尽量不要在JS中的添加过多的动态CSS,可以考虑使用addClass 针对JS中的事件: Keep JavaScript Out of HTML 避免以下的代码 ...
- java开发checklist,Java API设计CheckList
API设计原则:正确.好名.易用.易学.够快.够小.但我们从来不缺原则,〜〜〜 Interface 1.The Importance of Being Use Case Oriented,一个接口应当 ...
- C++ API 设计 06 第一章 简介
第一章 简介 1.1 应用程序编程接口是什么? 应用程序编程接口(API)提供对问题的一个抽象,并说明客户端如何与实现这个问题的解决方案的软件组件来进行交互. 这些组件本身通常作为一个软件库发布,允许 ...
- AOL架构原则.优秀API设计.Yeoman工具
本期的架构周报主要关注AOL(美国在线)的高可用性架构.技术专家Joshua Bloch对优秀API的设计观点.新的Web应用开发工具集Yeoman和OpenStack网络项目Neutron的介绍. ...
- 优秀的API接口设计原则及方法
一旦API发生变化,就可能对相关的调用者带来巨大的代价,用户需要排查所有调用的代码,需要调整所有与之相关的部分,这些工作对他们来说都是额外的.如果辛辛苦苦完成这些以后,还发现了相关的bug,那对用户的 ...
- 好RESTful API的设计原则
做出一个好的API设计很难.API表达的是你的数据和你的数据使用者之间的契约.打破这个契约将会招致很多愤怒的邮件,和一大堆伤心的用户-因为他们手机上的App不工作了.而文档化只能达到一半的效果,并且也 ...
- 优秀REST风格 API的设计原则
本文由逍遥子翻译自:https://codeplanet.io/principles-good-restful-api-design/ 文中注释由逍遥子根据个人理解填写,转载时请附带本文的连接. 设计 ...
- Atitit.论图片类型 垃圾文件的识别与清理 流程与设计原则 与api概要设计 v2 pbj...
Atitit.论图片类型 垃圾文件的识别与清理 流程与设计原则 与api概要设计 v2 pbj 1. 俩个问题::识别垃圾文件与清理策略1 2. 如何识别垃圾图片1 2.1. 体积过小文件<1 ...
最新文章
- zhs16gbk对应mysql_[Oracle] 彻底搞懂Oracle字符集
- C++知识点41——运算符的重载概念与分数类实现(下)
- 科普MinGW MinGW-W64
- 数据库 chapter 12 数据库管理系统
- JavaScript总结(六)
- input框在ios中的阴影问题
- C++(18)--复制构造函数
- 中科院遥感与数字地球研究所博士后招聘
- bzoj 2440: [中山市选2011]完全平方数【莫比乌斯函数+二分】
- Ubuntu 18.04 网易云音乐通过修改DPI 放大字体的方法
- NSString+NSMutableString+NSValue+NSAraay用法汇总
- 怎样网上赚钱最快方法有哪些?IP打造是关键
- poi 同时 冻结第一行和第一列
- 台式计算机加固态硬盘,台式电脑加固态硬盘教程_固态硬盘台式机安装方法-win7之家...
- JS匀速运动案例01
- 如何高效搜索公众号文章(r11笔记第35天)
- 基于tensorflow和卷积神经网络的电影推荐系统的实现
- WPF教程(十二)资源
- python指数函数表示_python实现画出e指数函数的图像
- 周鸿祎:没钱也能创业 怎样写商业计划书