高质量 developer 写高质量 code(javascript 版)

384 阅读5分钟

「本文已参与好文召集令活动,点击查看:后端、大前端双赛道投稿,2万元奖池等你挑战!

001.jpeg

速度至上的 web 开发

我们都知道 web 开发竞争,是没有更好只有最好的竞争,所以速度至上,这也是造成我们程序员加班的根源。如何在极速开发下来保证质量,这就需要提供我们

002.jpeg

什么是高质量的 code

003.png

高质量代码意味着,好的代码会吸引看下去,看完每行每句都会吸引你继续看下去,这是因为这些代码是由极具魅力的高质量 developer 开发的,高质量代码能够带来经济价值,降低后期维护成本,极高质量代码在开源以后将被广为流传。

  • 高性能: 深度理解 js 引擎原理,让您的 code 顺势而行,而不是逆势而上
  • 易维护: 复合公司内规范,便于公司内部其他人员接受,不至于新人来没有复用你的代码,而是因为遗留黑科技导致重写,造成成本浪费
  • 易扩展: 需求时时在变,所以在初期开发一定要考虑代码灵活性,以便轻松地拥抱未来变换
  • 可读性: 好的代码不需要注释,有时候并不是我们写不出巧妙代码,不是我们愚笨,而是让我们代码可读性更高。好的代码本身就是一篇精制文章。
  • 易测试: 可测试代码是以后我修改代码后应用不会炸的保证

如何写出高质量的 code

说了高质量 code 有点,那么如何成为一个可以写出高质量 code 的高质量 developer 呢? 我想这个问题大家会更加关心

老生常谈高内聚,低耦合

所谓高内聚,也就是让适当人去做适当事,不要一个类只去把一件事做好就行,将可以处理一件事的功能放在一起。那么低耦合呢? javascript 做弱类型动态语言天生就是 loose coupling ,少了接口和类这样约束。javascript 是按功能去设计,而不是按契约来设计的语言。

单一职责

当我们看到一函数长度几百行代码,我们就需要对其进行拆分,知道什么是对的不等价于做的就是对的。对于一个几百行甚至上千行的函数,通常会有一些功能或者说算法是可以提取共享给其他模块调用。double coding、triple coding ... 这样 long 函数就违背了上面两个高质量代码特性,难于维护和不易理解,而且难于 debug,自然也就是难于 test,自然也就不具有复用性,上面已经提到这一点,现在简单总结一下

  • 难于维护
  • 不容易理解
  • 难于 debug
  • 不便于 test
  • 无法复用

最后无法复用,只要 copy long 函数从而造成代码冗余,代码冗余还不是问题,问题是难于维护,一次调整多次修改。而且这样 long 函数也是典型低内聚,高耦合。

DRY(don't repeat yourself)

这些事作为高质量的 developer 要避免

其实 javascript 今天能成为语言排行榜第一名,当年 javascript 语言设计人可能也没有想到,其实 javascript 起初并不是精制的语言。我们随便想一想为什么今天 javascript 会如此流行呢? 首先简单容易上手,简单好用即便存在一些瑕疵,例如作用域的问题,只要不放大碍也不会阻碍 javascript 流行程度 ,还有就是互联网大环境还有就是 chrome 等浏览器不断优化,这些都为 javascript 成为 number one 奠定了基础。既然 javascript 本身存在一些瑕疵,我们要写出高质量代码就需要自行巧妙避开这些

如何判断真假

下面代码读完之后,看输出是否一些 confusing,

let a = "1";
let b = 1;
let c = "1.0";

console.log(a == b) //true
console.log(b == c) //true
console.log(a == c) //false

a 等于 b , b 等于 c 应该能够推出 a 等于 c,而在 javascript 给出的答案确让我们失望。作为一个高质量 developer 我们当然不会让这样 confusing 事情发生。

let a = "1";
let b = 1;
let c = "1.0";

console.log(a === b) //false
console.log(b === c) //false
console.log(a === c) //false

所以在判断两个变量相等时候,我们要 === 来代替 == 进行判断

切记使用变量前一定要去声明

function outer(){
    for (i = 0; i < 5; i++) {
        inner()
    }
}

function inner(){
    for (i = 0; i < 3; i++) {
        console.log("hello inner")
    }
}

outer()

运行代码会一直执行输出 hello inner,而我们的预期是输出 15 次 hello inner 不是一直执行下去。

function outer(){
    for (i = 0; i < 3; i++) {
        inner()
    }
}

function inner(){
    for (i = 0; i < 5; i++) {
        console.log("hello inner")
    }
}

outer()

我们让 outer 循环 3 次让 inner 循环 5 次,得到如下结果,为什么会是这样,主要原因是我们为 i 变量赋值前没有对其进行声明,所以这个 i 变量是一个全局变量

hello inner
hello inner
hello inner
hello inner
hello inner

如何解决这个问题呢,首先可以开启严格模式帮助我们检查代码中可能出现潜在的问题。为整个 javascript 文件开启严格模式,需要在所有语句之前放一个特定语句 "use strict"; (或 'use strict';

严格模式
  • 严格模式通过抛出错误来消除了一些原有静默错误
  • 严格模式修复了一些导致 JavaScript引擎难以执行优化的缺陷:有时候,相同的代码,严格模式可以比非严格模式下运行得更快
  • 严格模式禁用了在ECMAScript的未来版本中可能会定义的一些语法
"use strict"
function outer(){
    for (i = 0; i < 3; i++) {
        inner()
    }
}

function inner(){
    for (i = 0; i < 5; i++) {
        console.log("hello inner")
    }
}

outer()

添加为严格模式,这里多说一句如果是 javascript module 我们就无需添加严格模式了,添加完后运行代码就会 complie 失败给出如下提示

ReferenceError: i is not defined

而且这里我们用了 let 而不是 var,因为 var 不支持大括号作用域,而 let 是支持大括号作用域。所以推荐大家尽量使用 let 或者 const 这些新语法来声明变量

function outer(){
    for (let i = 0; i < 3; i++) {
        inner()
    }
}

function inner(){
    for (let i = 0; i < 5; i++) {
        console.log("hello inner")
    }
}

outer()

const 让我们的代码更安全

const 用于静态变量,定义后我们就无法再为变量赋值,这样就是 immutable 数据结构,这样好处就是增加代码确定性,从而提供代码安全性

"use strict"

let factor = 2;

let PrintIt = (e) => console.log(e*factor);

PrintIt(4)//8
"use strict"

let factor = 2;

let PrintIt = (e) => console.log(e*factor);

factor = 0;

PrintIt(4)//0
"use strict"

const factor = 2;

const PrintIt = (e) => console.log(e*factor);

factor = 0;

PrintIt(4)

函数式编程看起更优雅

指令式编程(Imperative programming)

我们需要告诉计算机我们要做什么,同时还需要告诉计算机怎么去做。看下面代码,每一件事都要我们自己去做,例如 number 数组长度是通过 size 还是 count 还是 length 获取,循环语句的终止条件以及每次循环更新操作,每件事都是需要我们面面俱到。而且指令式编程往往带来一些偶然性的复杂性,这是我们不想看到的,我们工作就是就是尽量用简单方式实现功能,减少复杂性。

const numbers = [0,1,2,3,4,5,6,7,8,9,10];

let result = 0

for (let i = 0; i < numbers.length; i++) {
    if(numbers[i] % 2 === 0){
        result += numbers[i]
    }    
}

console.log(result)

函数式编程好处是我们只需要告诉程序我们要做什么,无需告诉应该如何去做。

const numbers = [0,1,2,3,4,5,6,7,8,9,10];

const result =
    numbers.filter(function(e){ return e%2 == 0})
    .map(function(e){return e * 2})
    .reduce(function(total,e){return total + e})

console.log(result)

这样代码在可读性大大提高了,可能认为对于有一定经验的你来说,for 循环更好理解,这是因为大家看久用久 for 循环语句,其实这些方式编码对于从来没有接触过编程人并不友好,而是这样代码、先是筛选、然后操作每个元素,最后将操作后元素依次叠加更合理,而且我们不需要考虑一些细节,例如如何获取数组长度,什么是循环终止条件,以及每次循环如何更新条件等等。

还能再优雅一些吗

我们

const numbers = [0,1,2,3,4,5,6,7,8,9,10];

const result =
    numbers.filter((e)=>  e%2 == 0)
    .map((e)=> e * 2)
    .reduce((total,e)=> total + e)

console.log(result)

优雅从小事做起

  • 拆分函数,通过一些策略介绍循环的嵌套
  • 变量命名,给每一个变量和方法都起一个恰到好处的名字,说起来比较容易,不算难事,但是能够给每一个变量和方法都起一个恰到好处名称,还真能反应一个 developer 功底如何,可以反应 developer 工作几年,他看过多少源码。、
  • 尽可能将可测试代码进行抽离,便于单元测试

007.jpg

工欲善其事,必先利其器

工欲善其事,必先利其器,可以利用下面这些工具对代码进行检测,然后根据提示来修改代码提高编码质量,因为对于我们即使工作有一定年限有经验的程序员也无法做到面面俱到,所以合理利用这些代码质量检测工具可以帮助我们提升代码质量

  • ESLint
  • JSLint
  • JSHint