js find的用法_JS 条件判断小技巧(二)

244abd10625dc65851f246b4c76b1a92.png

e6e965099c99ca180db1e3c017609d61.png


我前面发过一篇关于条件判断小技巧的文章,今天接着聊。所谓小技巧,说实在就是特定场景里的特例手段;对于具备一定开发能力的码农,这些特例基本都能避开。但是,某些业务逻辑本身就十分复杂,嵌套的条件语句在逻辑层面就不可能有所优化了;碰到这类场景,我们又该如何作为呢?

function greeting(role, access) {  if( 'owner' ===  role ){    if( 'public' === access ){        ...    }    if( 'private' === access ){        ...    }    ...  } else if ( 'admin' ===  role ){    if( 'public' === access ){        ...    }    if( 'private' === access ){        ...    }    ...  } else if( 'hr' === role ) {    ...  }}

看一下代码,第一层的if-else判定的是各种角色(role)类别,第二层判定的是角色访问权限设置(access)。这类代码其实并没有特别优雅的处理手段,只能回到《clean code》里最本源的解决手段——把函数写小。本质问题还是函数体过大,而所谓的把大函数拆成多个小函数,事实上就是以抽象换取可读性。

我自己是一名从事了多年开发的web前端老程序员,目前辞职在做自己的web前端私人定制课程,今年年初我花了一个月整理了一份最适合新手学习的2019年学习的web前端学习干货,从最基础的HTML+CSS+JS到HTML5 的项目实战的学习资料都有整理,送给每一位前端小伙伴,想要获取的可以添加我的web前端交流qun,【六零零】+【六一零】+【一五一】,直接在群里下载,对于学习web前端有任何问题(学习方法、学习效率、如何就业)都可以问我。

OOP 多态

最常规的手段就是 OOP 多态了。上述代码块,第一层的 role 抽象为 User 实例,嵌套层内的各种 access 进一步抽象为 User 的实例方法。

class User {  public() {    throw new Error('Denied!')  }  private() {    throw new Error('Denied!')  }}

Javascript 并没有 interface 这类语法,好在有 class 了,我仿造 interface 写了一个基类如上。接着就是将各种角色抽象为新的子类型:

class Owner extends User {  public() {    console.log('Owner in public');  }  private() {    console.log('Owner inside');  }}class Admin extends User {  public() {    console.log('Admin in public');  }  private() {    console.log('Admin inside');  }}...

OOP 推荐使用工厂方法初始化实例,我顺手也写个工厂,这样便可以利用工厂方法消除掉了第一层if-else

class UserFactory {  static create(role) {    if( 'owner' === role )      return new Owner();    else if( 'admin' === role )      return new Admin();    ...  }}

调用的时候我们先通过 role 创建抽象实例,再根据 access 调用具体方法:

function greeting(role, access) {  const user = UserFactory.create(role);  user[access]();}

上面一长串的if-else,一下子被压缩到了两行。这就实现了以抽象(很多可描述的类)换取了可读性(较少的判断嵌套)

调用链

OOP 效果确实很明显,不过上述代码还是过于特例,假如access并不是字符串(如1,2,3),像user[1]这种就很难映射到具体方法了;所以我们往往还要写更细碎的 access 抽象,也便意味着更多的抽象子类,以及新的工厂方法。很多时候,我们也不并需要抽象得尽善尽美。这个场景里写个调用链,也是勉强可用的:

const rules = [  {    match(role, access) {      return 'owner' === role;    },    action(role, access) {      if( 1 === access )        console.log('Owner in public');      else if( 2 === access )        console.log('Owner in private');    }  },  {    match(role, access) {      return 'admin' === role;    },    action(role, access) {      ...    }  }  ...];

上面 rules 数组里,每一个元素(rule)里的match被设计用来判定用户权限:遍历数组,若是match为 true,则运行正下方的action——access 相关业务;反之,继续match下一个 rule:

function greeting(role, access){  rules.find(e => e.match(role)).action(role, access)}

最后 greeting 被重构为上述代码。当然,效果没有多态好,只消掉了一层if-else,第二层判定还是留在了 action 里。

AOP

AOP,没看错,Javascript 也是有 AOP 的,只是它的实现要修改 Function 的原型链,不是很推荐;但是Function.prototype.before,Function.prototype.after还是挺常见的,开发组里能协商好,还是可以尝试一下的:

Function.prototype.after = function(next) {  let fn = this;  return function $after(...args) {    let code = fn.apply(this, args)    next.apply(this, args);    return code;  }}

传统的 aop after 如上所示。不难看出,用到了高阶函数:具体执行时,先运行函数本体,再运行 after 传进来的 next 方法。为了让 after 应用到我们的话题中,我稍微改一下函数实现:

const nextSmb = Symbol('next');Function.prototype.after = function(next) {  let fn = this;  return function $after(...args) {    let code = fn.apply(this, args)    if( nextSmb === code )      return cnext.apply(this, args);    return code;  }}

这个 after 实现变成了先运行函数本体,若返回是nextSmb则继续执行后续的 next 方法,反之则停止。有什么用呢?我们看看如何使用:

function owner (role, access) {  function public(access) {    return 1 === access ? console.log('owner in public') : nextSmb;  }  function private(access) {    return 2 === access ? console.log('owner in private') : nextSmb;  }  const ownerChain = public.after(private);  return 'owner' === role ? ownerChain(access) : nextSmb;}

代码还是有点难度的,先看一部分——owner 的定义。这个函数被设计处理role === 'owner'时的逻辑,内部的public和private方法是处理access为 1 和 2 时的逻辑。我们把public和private方法串联成ownerChain(终于用到after方法了),它的作用就是把之前的if-else逻辑抽象成一个上节讲到的函数调用链,在遍历调用链时检查 access 条件:若符合条件,则执行本节点代码,并结束调用链;反之,继续往调用链的后续节点传送。

我把重构后的 greeting 也列一下——单个role的access可以用after串联;不同role之间也可以进一步利用after串起来。

function admin (role, access) {  // familiar with owner}let greeting = owner.after(admin)greeting('owner', 1);

嗯,这样,我们最原始的greeting方法就被彻底重构了。可以预见,如果调用链很长greeting会是这样:

let greeting = owner.after(admin).after(hr).after(staff)...

当然这个方法缺点也很明确,比起之前冗长的代码,可读性增强了,但是理解成本有点高,若团队内没有事先约定,这个维护起来还是挺难的。

ramda

ramda是我很喜欢用的一个方法库,在 github 上有大约 18K 的 star,它提供了一整套 FP 方法。比起上面调用链aop这种野路子,ramda 库更适合在团队内推广。我们试着用 ramda 重写一下上面提到的greeting方法:

const R = require('ramda')const ownerChain = R.cond([  [(role, access) => 1 === access, () => console.log('owner in public')],  [(role, access) => 2 === access, () => console.log('owner in private')],])const adminChain = R.cond([ ... ])const greeting = R.cond([  [R.equals('owner'), ownerChain],  [R.equals('admin'), ownerChain],])

我想大家即便没用过 ramda,也能大体猜出代码用法吧。R.cond类似于上面用到的调用链实现:二维数组第一列就是 match 函数,做判定;第二列就是 action 函数,用于执行嵌套逻辑。若嵌套较深,可以像ownerChain和adminChain一样再实现一套R.cond调用链。

我们再将 FP 的 ramda 实现与上面 OOP 多态做个比较:OOP 将逻辑抽象为对象,FP 则是抽象为更小的函数。通常来说 FP 的代码更加精简,但是学习成本更高:如果没有专项训练,你根本看不懂 FP 代码,更别说码代码了。我自己部门里也有写半吊子 FP 的团队,最后写出来的代码长得像迎客松一样,并没有比多层嵌套的条件语句美观多少。

小结

本文在之前if-else小技巧的基础上,介绍了一些更通用场景里的优化方式。(当然,有些方式哗众取宠了)虽然大篇幅介绍了一些野路子,但最终还是推荐大家学习正统 OOP、FP 的解决方案。学生时代,我们很少接触代码,还没事还喷喷书本知识;工作后,见识多了,才发现前人的经验弥足珍贵。

b41926496eaea7841373c35eee6d8ac8.png
作者:anOnion
链接: https://www. jianshu.com/p/6024b1796 af5
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
find_in_set函数是MySQL中的一个函数,用于在一个字符串列表中查找指定的字符串。它的语法是FIND_IN_SET(str, strlist)。其中,str是要查询的字符串,strlist是一个以逗号分隔的字符串列表。如果str在strlist中,则返回值是str在strlist中的位置;否则返回0。 在MySQL中,当find_in_set函数的第一个参数是一个常数字符串,而第个参数是SET类型的列时,MySQL会对这个函数进行优化,使用比特计算来提高查询效率。 例如,在一个名为article的表中,我们可以使用find_in_set函数进行多条件查询。比如,我们可以使用以下语句来查询type列中包含字符串'4'的所有记录: SELECT * FROM article WHERE FIND_IN_SET('4', type) 这样,MySQL将会返回type列中包含字符串'4'的所有记录。 所以,使用find_in_set函数进行多条件查询可以优化查询效率,并且可以方便地在字符串列表中查找指定的字符串。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* [mysql中find_in_set()函数的使用及in()用法详解](https://download.csdn.net/download/weixin_38699352/13699437)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 33.333333333333336%"] - *2* [mysql中find_in_set()函数的使用详解](https://download.csdn.net/download/weixin_38656395/13699347)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 33.333333333333336%"] - *3* [MySQL的FIND_IN_SET函数使用方法分享](https://download.csdn.net/download/weixin_38622227/13725311)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 33.333333333333336%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值