javascript的api设计原则

简介: javascript api

前言

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

一、接口的流畅性

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

1.简单

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

1

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

封装之后

1

2

3

4

functiona(selector, color) {

  document.querySelector(selector).style.color = color

}

a('#a''red');

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

2.可阅读性

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

1

2

functionletSomeElementChangeColor(selector, color) {

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

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

3.减少记忆成本

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

1

2

3

functionsetColor(selector, color) {

 xxxxxxxxxxxx

}

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

4.可延伸

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

1

2

3

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方法封装成对象,然后再对象的每个方法中返回这个对象:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

functiongetElement(selector) {

  this.style = document.querySelecotrAll(selector).style;

}

 

getElement.prototype.color = function(color) {

  this.style.color = color;

  returnthis;

}

getElement.prototype.background = function(bg) {

  this.style.backgroundColor = bg;

  returnthis;

}

getElement.prototype.fontSize = function(size) {

  this.style.fontSize = size;

  returnthis;

}

 

//调用

varel = newgetElement('#id')

el.color('red').background('pink').fontSize('12px');

简单、流畅、易读,它们看起来就像行云流水一样,即在代码性能上得到了提升优化,又在视觉上悦目。后面我们会在参数里面讲到如何继续优化。

所以,大家都比较喜欢用jquery的api,虽然一个$符号并不代表任何现实意义,但简单的符号有利于我们的使用。它体现了以上的多种原则,简单,易读,易记,链式写法,多参处理。

nightmare:

1

2

3

document.getElementById('id').style.color = 'red';

document.getElementById('id').style.fontSize = '12px';

document.getElementById('id').style.backgourdColor = 'pink';

dream:

1

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

二、一致性

1.接口的一致性

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

Nightmare:

1

2

3

4

setColor,

letBackGround

changefontSize

makedisplay

dream:

1

2

3

4

setColor;

setBackground;

setFontSize

set.........

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

三、参数的处理

1.参数的类型

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

1

2

3

4

5

//我们规定,color接受字符串类型

functionsetColor(color) {

  if(typeofcolor !== 'string'return;

dosomething

}

2.使用json方式传参

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

1

functionfn(param1, param2...............paramN)

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

1

2

3

4

5

6

7

8

functionfn(json) {

//为必须的参数设置默认值

   vardefault= extend({

    param: 'default',

    param1: 'default'

    ......

   },json)

}

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

四、可扩展性

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

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

//需要同时改变某个元素的字体和背景

// Nightmare:

functionset(selector, color) {

  document.querySelectroAll(selector).style.color = color;

  document.querySelectroAll(selector).style.backgroundColor = color;

}

 

//无法扩展改函数,如果需要再次改变字体的大小的话,只能修改此函数,在函数后面填加改变字体大小的代码

 

//Dream

functionset(selector, color) {

  varel = document.querySelectroAll(selector);

  el.style.color = color;

  el.style.backgroundColor = color;

  returnel;

}

 

//需要设置字体、背景颜色和大小

functionsetAgain (selector, color, px) {

  varel = set(selector, color)

  el.style.fontSize = px;

  returnel;

}

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

2.this的运用

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

1

2

3

4

5

6

7

8

9

functionsayBonjour() {

  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。

1

2

3

4

5

6

7

8

9

10

11

12

13

functionerror (a) {

  if(typeofa !== 'string') {

    console.error('param a must be type of string')

  }

}

 

functionerror() {

  try{

    // some code excucete here maybe throw wrong

  }catch(ex) {

    console.wran(ex);

  }

}

六、可预见性

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

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

//nighware

functionset(selector, color) {

  document.getElementById(selector).style.color = color;

}

 

//dream

zepto.init = function(selector, context) {

  vardom

  // If nothing given, return an empty Zepto collection

  if(!selector) returnzepto.Z()

  // Optimize for string selectors

  elseif(typeofselector == '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

    elseif(context !== undefined) return$(context).find(selector)

    // If it's a CSS selector, use it to select nodes.

    elsedom = zepto.qsa(document, selector)

  }

  // If a function is given, call it when the DOM is ready

  elseif(isFunction(selector)) return$(document).ready(selector)

  // If a Zepto collection is given, just return it

  elseif(zepto.isZ(selector)) returnselector

  else{

    // normalize array if an array of nodes is given

    if(isArray(selector)) dom = compact(selector)

    // Wrap DOM nodes.

    elseif(isObject(selector))

      dom = [selector], selector = null

    // If it's a html fragment, create nodes from it

    elseif(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

    elseif(context !== undefined) return$(context).find(selector)

    // And last but no least, if it's a CSS selector, use it to select nodes.

    elsedom = zepto.qsa(document, selector)

  }

  // create a new Zepto collection from the nodes found

  returnzepto.Z(dom, selector)

}

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

七、注释和文档的可读性

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

1

2

3

4

5

6

7

8

9

10

//注释接口,为了演示PPT用

functioncommentary() {

  //如果你定义一个没有字面意义的变量时,最好为它写上注释:a:没用的变量,可以删除

  vara;

 

  //在关键和有歧义的地方写上注释,犹如画龙点睛:路由到hash界面后将所有的数据清空结束函数

  returngo.Navigate('hash'function(){

    data.clear();

  });

}

目录
相关文章
|
1月前
|
JavaScript 前端开发 API
常用JavaScript 数组 API大全
常用JavaScript 数组 API大全
32 0
|
1月前
|
API
egg.js 24.2写第一个api接口
egg.js 24.2写第一个api接口
71 0
|
4月前
|
敏捷开发 JavaScript 前端开发
❤❤❤【Vue.js最新版】sd.js基于jQuery Ajax最新原生完整版for凯哥API版本❤❤❤
❤❤❤【Vue.js最新版】sd.js基于jQuery Ajax最新原生完整版for凯哥API版本❤❤❤
|
3月前
|
分布式计算 JavaScript 前端开发
JS中数组22种常用API总结,slice、splice、map、reduce、shift、filter、indexOf......
JS中数组22种常用API总结,slice、splice、map、reduce、shift、filter、indexOf......
|
2月前
|
Web App开发 JavaScript NoSQL
深入浅出:构建基于Node.js的RESTful API
在当今快速发展的互联网时代,RESTful API已成为前后端分离架构中不可或缺的一部分。本文旨在为初学者和中级开发人员提供一个清晰、简洁的指南,详细介绍如何使用Node.js构建一个高效、可维护的RESTful API。通过结合实际案例,本文将从API设计理念出发,深入讲解如何利用Express框架及MongoDB数据库实现API的增删改查功能,同时探讨如何通过JWT进行安全认证,确保数据传输的安全性。此外,文章还将简要介绍如何使用Swagger生成API文档,使得API的测试和维护更加便捷。无论你是希望提升现有项目的API设计,还是想从零开始构建一个新项目,本文都将为你提供一条清晰的道路
|
30天前
|
API 开发者 UED
深入探讨RESTful API设计原则及最佳实践
在当今互联网时代,RESTful API已成为各种软件系统之间进行通信的重要方式。本文将从资源定义、URI设计、HTTP方法选择、状态码规范等方面深入探讨RESTful API设计的原则与最佳实践,帮助开发者更好地构建高效、健壮的API。
|
1月前
|
JavaScript 前端开发 API
JavaScript循环遍历常用的7种方法以及常用的数组 API
JavaScript循环遍历常用的7种方法以及常用的数组 API
34 0
|
1月前
|
Web App开发 JavaScript 前端开发
使用Node.js和Express构建RESTful API
使用Node.js和Express构建RESTful API
16 0
|
2月前
|
API 开发者 UED
深入理解RESTful API设计原则
【2月更文挑战第9天】 传统的RESTful API设计原则在实际开发中并不是一成不变的,随着技术的发展和应用场景的变化,我们需要不断深入理解RESTful API的设计原则,并结合具体情况进行灵活应用,以更好地满足现代应用的需求。
26 4
|
2月前
|
NoSQL JavaScript 前端开发
深入浅出:使用Node.js和MongoDB构建RESTful API
在当今的软件开发领域,构建高效、可扩展的Web服务已成为开发者的重要任务之一。本文将引导读者通过现代JavaScript环境——Node.js,搭配流行的NoSQL数据库MongoDB,一步步构建一个RESTful API。不同于常规的摘要,我们将采用故事化的方式,通过一个虚构的应用“BookFinder”的开发旅程,带领读者理解API设计、数据库交互及安全性考量等关键知识点。无论你是前端工程师希望深化后端知识,还是后端新手寻求实践机会,本文都将提供你所需的指南和启示。