Skip to content

一行代码写一个接口

2018-8-24

原文

今天的一个业务需求,逻辑特别简单,前端给传来openid,去数据库匹配取值再返回给前端就好了。

这个需求中需要经历这样的流程:

  1. 接收前端传值

  2. 去数据库取值

  3. 返回给前端

机智的我发现,这个需求如果想写得精致一些,还可以再加一步:

  1. 验证前端传值

这个需求,如果给别人写,可能就这样中规中矩过去了:

2

由于事情不是很多,还有比较闲,我就想怎样能一行代码搞定这个需求。接下来我就描述一下整个心路历程。

  • 首先这个if和else是可以使用三元运算符解决的;

  • 参数验证让我想到了短路运算符;

  • 中间的函数执行我想使用立即执行函数;

这三个想法,只有第三个有php版本的风险,于是我登录了服务器,查询了一下php版本,发现生产环境的php版本是7+,那么立即执行函数是可行的。

3

好,说干就干。

这是我写的第一版“一行代码写一个接口”:

4

这样看来,还是换行了。没关系,这是编辑器的自动换行。我直接把代码粘贴过来。

!empty(I('post.open_id')) && (function(){echo json_encode(($result = M('member') -> where(['member_open_id' => I('post.open_id')]) -> find())?['code' => 1,'data' => $result]:['code' => 0,'msg' => '查无此人']);exit;})() || (function(){echo json_encode(['code' => 0,'msg' => '缺少参数']);})();

我跟一起划水的小冯同学交流了一波感情,毕竟立即执行函数和短路运算符这些变态东西都是他们前端经常用的。小冯看了之后发表了重要讲话:

5

这个确实是个问题,由于想装逼而写了这么复杂的代码,还要受返回值的约束,着实不是很实用。

但是这个坑在我们本次的需求中是不存在的,因为我们在匿名函数中只执行了某个操作,随后程序就exit了。不存在返回值的问题。所以,如果不涉及返回值,这种写法还是可以秀一下的。

然后小冯同学提出了改进意见:

6

7

然后我就改进出了第二版:

!empty(I('post.open_id')) ? (function(){echo json_encode(($result = M('member') -> where(['member_open_id' => I('post.open_id')]) -> find())?['code' => 1,'data' => $result]:['code' => 0,'msg' => '查无此人']);exit;})() : (function(){echo json_encode(['code' => 0,'msg' => '缺少参数']);})();

经过测试发现完美运行。

经过两波折腾之后,代码已经变成了这个样子…

8

外包公司应该没有代码审计的问题,所以我决定留下我的第一版代码。不知道下一个改这段代码的是哪路神仙,没准到时候我已经跑路了。

在本次折腾中,涉及到了这么几个知识点:

  • 使用三元运算符取代if…else

  • 立即执行函数

  • 短路运算符

那么问题来了,什么是立即执行函数,什么是短路运算符。我就听说过一个三元运算符,怎么把三元运算符跟ifelse结合起来?

还有半小时下班,我就挨个打一打。划水划到下班。

三元运算符

学过编程或者上过编程课的同学应该都听过三元运算符,就算没听过这个名字,也一定见过这个运算符———— ? :

对,一个问号一个冒号,就是三元运算符。为了表达三元运算符的作用,有这两块代码:

if(a){

    b();

}else{

    c();

}
a?b():c();

这两块代码是等价的。

立即执行函数

在常规的认知中,函数都是这个样子的:

function a(){

    do_something();

}

在想要使用这个函数的时候,这样子来调用:

a();

如果想让这个函数在被声明的同时就调用,可以这样写:

(function(){do_something();})();

这一行代码可以这样去理解,前面括号里是一个匿名函数的声明,后面一个括号是去调用这个函数。即

9

这样写代码肯定有人会说,哦你这种写法确实秀得一批,但是又有什么用呢?好好的一个函数都写到一行了,睡一觉起来你还能看得懂么?

这样写代码最大的好处就是,避免变量污染。

为了讲解变量污染这个问题,我们需要将语言天赋切换到js。如果对于这两种语言都不太了解的话,就无视我语言切换这句话,没啥卵用。

看这样一个问题:

var liList = ul.getElementsByTagName('li')
for(var i=0; i<6; i++){
  liList[i].onclick = function(){
    alert(i) // 为什么 alert 出来的总是 6,而不是 0、1、2、3、4、5
  }
}

因为i这个变量是整个作用域通用的,换句话说就是,全局只有一个i。循环过去了之后,i就变成了6。虽然每个li元素各自见过i是12345的样子,但最后我们唯一的i变成了6,谁都拦不住。

这段代码可以怎样改呢?

var liList = ul.getElementsByTagName('li')
for(var i=0; i<6; i++){
  !function(ii){
    liList[ii].onclick = function(){
      alert(ii) // 0、1、2、3、4、5
    }
  }(i)
}

这样一来,i在每次变化之前都传入一个匿名函数中,被临时保存在了ii中。每一个独立的ii记住了i当初的样子。就算i最后变成了6,当初的12345也被独立的ii们保留了。

在js中,经常用立即执行函数来起到命名空间的作用。在es6语法中,可以使用export来解决同样的问题。在之前的代码中,框架大佬们经常使用这种方式来封装框架,来避免变量污染。例如jQuary的开头是这种诡异的形式,你根本不知道常用的$在哪

还有mui的核心js将document对象传入了一个匿名函数中…

11

12

短路运算符

还有十分钟就要下班了,最后叨咕叨咕短路运算符。

短路运算符就是下面这种,在js中很常见,但一般都是这种:

cb && cb();

通常cb是回调函数,这句代码的意思就是如果cb存在则执行。实际上也是实现了ifelse的取代。上面这段代码也可以这样写:


if(cb){

    cb();

}

短路运算符实际上是利用了&&运算的结合顺序,如果&&运算符左边的表达式是假,那么右边的表达式就不会被执行。

如果逻辑与运算符左边的值布尔转换后为true,那么返回右边的值。如果逻辑与运算符左边的值布尔转换后为false,那么返回左边的值,但是当逻辑与的左边为 null/NaN/undefined ,结果就会得到null/NaN/undefined。

既然都说到这了,我们就连同逻辑或的运算逻辑一起复习一下。

如果逻辑或运算符左边的值布尔转换后为false,那么返回右边的值(不管右边的值是真还是假)。如果逻辑或运算符左边的值布尔转换后为true,那么返回左边的值,如果两个操作数都是是null(NaN/undefined),返回null(NaN/undefined)。

本次需求中我们需要连else里的内容也一起取代了,所以就有了后面的升级版。

cb && cb() || cb2();

代码打多了,写什么都想给放到一行里。

使用公司里给的九块九包邮咸鱼键盘打字打得手筋累,决定下班回去休息一波看看海。今天的程序员秀你一脸作死大法就到这里,下期再见。

参考资料:

  1. JS中短路运算符&&和||

https://blog.csdn.net/k491022087/article/details/52404275

  1. 什么是立即执行函数?有什么作用?

https://www.cnblogs.com/mafeifan/p/5881408.html

  1. mui.min.js、mui.js、jquery-3.3.1.min.js源码。

最后更新于: