《代码整洁之道》(二) ---函数

106 阅读3分钟

函数

短小

每个函数应尽量保持短小,且依次的将你带到下一个函数

if语句,else语句,while语句等,其中的代码应该只占用一行,该行一般应是一个函数调用语句,这样可以保证函数足够短小

只做一件事

什么是只做一件事: 如果函数只是做了函数名下同一抽象层的步骤,则函数还是只做了一件事.

每个函数一个抽象层级

要确保一个函数只做一件事,同一个函数中的语句就应该在同一个抽象级上.每个函数只做它这一抽象级的事情.

什么是一个抽象层级?

if(arg < 0){
    ...
}else {
    ...
}
上述代码中if-else就是一个层级,这个层级只用来做判断,而括号中做的事情就是下一个层级,因此封装成函数时应该是这样
function fn(arg) {
    if(arg < 0){
        函数1
    }else {
       函数2
    }
}

自顶向下读代码: 向下规则

想让我们的代码拥有自顶向下的阅读顺序,就要让每个函数后面跟着位于下一抽象层级的函数,当函数之间相互调用的时候,顶层函数的抽象级范围应该是最大的,而且随着函数调用的层级越深,函数的抽象层级理应越小,这也就是向下规则,

这是保持函数短小,只做一件事的要诀

函数名称

给函数起一个能描述清功能的名字,不要担心长名称,长而具有描述性的名称,要比短而令人费解的名称好,要比描述性的长注释好

最好使用某种命名约束如:驼峰命名法,单词简写等让名称中的多个单词容易阅读,

结合只做一件事和每个函数一个抽象层级,这三者最终产生的效果应该是: 随着函数调用层级越来越深,函数的抽象层级越来越小,函数做的事情粒度越来越小,而函数的命名则越来越长.

最好的实践就是对同一个函数尝试不同的名称,实测其阅读效果,使用效果最好的那个

分隔开指令和询问

函数要么做什么事,要么回答什么事,两者不能兼得.

使用异常

  • 抽离try/catch代码块

    抽离try和catch中的代码,形成函数

    try{
        deletePage();
        deleteAttribute()
    }catch(e){
        throw e
    }
    抽离:
    try{
        deletePageAndAttribut()
    }catch(e){
        throwError(e)
    }
    function deletePageAndAttribut(){
        deletePage();
        deleteAttribute()
    }
    function throwError(err){
        throw err
    }
    
    
  • 错误处理就是一件事

    遵循单一职责原则,处理错误的函数不应该进行其他活动.也就是说一个函数中只能由try/catch/finally的逻辑,不再由其他的

使用解释变量

顾名思义,解释变量就是用来解释他是干什么的,没有其他用处.什么场景下需要使用解释变量呢?最最常见的就是在判断的时候:

function fn(idx){
    if(idx > 0 && idx < 10){
        do...
    }else{
        do....
    }
}
看上述代码,如果我们不去读括号中的逻辑,那么永远也不知道(idx > 0 && idx < 10)这个判断条件是什么意思,这时候用上解释变量,立马就上代码的可读性上升了一个档次:
function fn(idx){
    let isSingleDigit = idx > 0 && idx < 10;
    if(isSingleDigit){
        do...
    }else{
        do....
    }
}

如何写出这样的函数

精致的代码是不断打磨出来的,一开始往往都是冗长而复杂,大量的嵌套,大量的循环,当初稿完成后,再对其斟酌推敲,分解函数?修改名称?消除重复?等等,即要考虑实现业务逻辑,又要实现精致代码是比较困难的,因此,先完成初稿,然后才去琢磨规则