论如何优雅的写出不会挨揍的代码

5,129 阅读11分钟
参照github明星项目之一 `nocode`,给出以下建议。

No Code

No code is the best way to write secure and reliable applications. Write nothing; deploy nowhere.

Getting Started

Start by not writing any code.


This is just an example application, but imagine it doing anything you want. Adding new features is easy too:


The possibilities are endless.

Building the Application

Now that you have not done anything it's time to build your application:


Yep. That's it. You should see the following output:


Deploying

While you still have not done anything it's time to deploy your application. By running the following command you can deploy your application absolutely nowhere.


你会不会被同事打死我不知道,但你会被老板打死这是肯定的。

好了,上面只是开个玩笑,在这里附上nocode的地址,大家有空可以瞻仰一下。

现在进入正题。很多时候我们会遇到修改其他人的代码或在对方代码上添加功能的情况,我想大部分人当时心里一定是这样的。


那如何尽可能的保全自己呢?

  1. 谢同事不杀之恩,平时多投喂些零食。
  2. 写出好代码。

我个人希望你能静下心来仔细的品一遍,如果你有收获,请给作者点个赞吧,让我知道还有人从这里获得了知识。虽然很琐碎,也可能你会浪费人生中宝贵的八分钟。

向盟约宣誓~~~~

语言规范

引用

const 和 let 都是块级作用域,var 是函数级作用域

  • 对所有不可变引用都使用 const,可变引用使用let,不要使用 var
// bad
var a = 1
var b = 2
if (b < 10) {
  b += 1
}

// good
const a = 1
let b = 2
if (count < 10) {
  count += 1
}
  • 链式赋值 链式赋值容易造成代码的可读性差,约定禁止使用链式赋值
不推荐

let a = b = c = 1
推荐

let a = 1
let b = 1
let c = 1

对象

  • 请使用字面量值创建对象
// bad
const a = new Object{}

// good
const a = {}
  • 请使用对象属性值的简写方式
  • 对象属性的每一行末尾添加·逗号·,详见下方 拖尾逗号
  • 变量的声明后追加分号,若遵循 Standard 的规范,忽略本条。
  • 如已有统一规范,请忽略上述两条。
const job = 'FrontEnd';

// bad
const item = {
  job: job
}

// good
const item = {
  job,
};

你永远不能保证这个对象会不会被追加属性,所以热情的帮你的同事补上每行的·逗号·吧~
否则如果你同事刚好开启了代码检查,他很可能会来揍你

数组

  • 使用字面量值创建数组
  • 向数组中添加元素时,请使用 push 方法
// bad
const items = new Array();
items[items.length] = 'test';


// good
const items = [];
items.push('test');

  • 使用拓展运算符 ... 复制数组
const items = [];
const itemsCopy = [];
const len = items.length;
let i;

// bad
for (i = 0; i < len; i++) {
  itemsCopy[i] = items[i];
}

// good
itemsCopy = [...items];

嗯。。。我觉得现在应该见不到bad方式的写法了吧。。大概?

  • 使用forEach、some、every等代替for循环
const items = [];
const len = items.length;

// bad
for (i = 0; i < len; i++) {
 // todo something for items[i]
}

// good
items.forEach((item, index) => {
    // todo something for items
});
  • 使用数组的 map方法时,请使用 return 声明,如果是单一声明语句的情况,可省略 return
// good
[1, 2, 3].map(x => {
  const y = x + 1
  return x * y
});

// good
[1, 2, 3].map(x => x + 1);
  • 使用数组的 reduce方法时,请赋予初值,并使用 return 声明,如果是单一声明语句的情况,可省略 return
// good
const flat = [[0, 1], [2, 3], [4, 5]].reduce((memo, item, index) => {
  const flatten = memo.concat(item)
  return flatten
}, []);

我知道在座的都是大佬,但万一其中混入了奇怪的东西呢?

  • 使用数组的 filter方法时,使用单一判断,保证最后总会返回false/true
// bad
inbox.filter((msg) => {
  const { subject, author } = msg
  if (subject === 'Mockingbird') {
    return author === 'Harper Lee'
  } else {
    return false
  }
})

// good
inbox.filter((msg) => {
  const { subject, author } = msg
  if (subject === 'Mockingbird') {
    return author === 'Harper Lee'
  }
  return false
})

解构赋值

  • 当需要使用对象的多个属性时,请使用解构赋值
// bad
function getFullName (user) {
  const firstName = user.firstName
  const lastName = user.lastName

  return `${firstName} ${lastName}`
}

// good
function getFullName (user) {
  const { firstName, lastName } = user

  return `${firstName} ${lastName}`
}

// better
function getFullName ({ firstName, lastName }) {
  return `${firstName} ${lastName}`
}
  • 当需要使用数组的多个值时,请同样使用解构赋值
const arr = [1, 2, 3, 4]

// bad
const first = arr[0]
const second = arr[1]

// good
const [first, second] = arr
  • 函数需要回传多个值时,请使用对象的解构,而不是数组的解构
// bad
function doSomething () {
  return [top, right, bottom, left]
}

// 如果是数组解构,那么在调用时就需要考虑数据的顺序
const [top, xx, xxx, left] = doSomething()

// good
function doSomething () {
  return { top, right, bottom, left }
}

// 此时不需要考虑数据的顺序
const { top, left } = doSomething()

经典情况:

// 在使用callback形式的时候,我经常会想,我定义的第一个参数是err还是data。。。
function handle(){
    // todo something
    if(err){
        return [null,err];
    }
    // todo something
    return [data,err];
}

function handle1(){
    // todo something
    if(err){
        return [err,null];
    }
    // todo something
    return [err,data];
}

字符串

  • 字符串统一使用单引号的形式 ''
// bad
const department = "JDC"

// good
const department = 'JDC'
  • 字符串太长的时候,请不要使用字符串连接符换行 \,而是使用 +
const str = '一键三连~' +
  '键三连~ 三连~ ' +
  '连~'
  • 程序化生成字符串时,请使用模板字符串
const test = 'test'

// bad
const str = ['a', 'b', test].join()

// bad
const str = 'a' + 'b' + test

// good
const str = `ab${test}`
  • 读取字符串中的字符时使用标准方法
// 优先使用 
string.charAt(3) 

// 而不是 
string[3]

函数

  • 不要使用 arguments,可以选择使用 ...

arguments 只是一个类数组,而 ... 是一个真正的数组

// good
function test (...args) {
  return args.join('')
}
  • 不要更改函数参数的值
// bad
function test (opts) {
  opts = opts || {}
}
// bad
function test1 (opts) {
  if(!ops){
      ops = {};
  }
}

// good
function test (opts = {}) {
  // ...
}

编码规范

单行代码块

  • 在单行代码块中使用空格
// 不推荐
function foo () {return true}
if (foo) {bar = 0}

// 推荐
function foo () { return true }
if (foo) { bar = 0 }

大括号两侧都要有空格,但每行末尾不要有空格
个人不推荐单行代码块,因为总会碰到向里面追加语句情况

大括号风格

在编程过程中,大括号风格与缩进风格紧密联系,用来描述大括号相对代码块位置的方法有很多。在 JavaScript 中,主要有三种风格,如下:

// One True Brace Style
if (foo) {
  bar()
} else {
  baz()
}

// Stroustrup
if (foo) {
  bar()
}
else {
  baz()
}

// Allman
if (foo)
{
  bar()
}
else
{
  baz()
}

建议使用第一种风格,请注意大括号两侧的空格,行头及末尾不添加空格

变量命名

当命名变量时,主流分为驼峰式命名(variableName)和下划线命名(variable_name)两大阵营。

约定变量及形参使用驼峰式命名
因为涉及后端开发,补充约定数据库参数使用下划线命名

逗号

  • 拖尾逗号
    在 ECMAScript5 里面,对象字面量中的拖尾逗号是合法的,但在 IE8(非 IE8 文档模式)下,当出现拖尾逗号,则会抛出错误。

拖尾逗号的例子:

var foo = {
  name: 'foo',
  age: '22',
}

拖尾逗号的好处是,简化了对象和数组添加或删除元素,我们只需要修改新增的行即可,并不会增加差异化的代码行数。

约定在最后一个元素或属性与闭括号 ] 或 } 在不同行时, 建议使用拖尾逗号。
当在同一行时,禁止使用拖尾逗号。

  • 逗号空格

逗号前后的空格可以提高代码的可读性,团队约定在逗号后面使用空格,逗号前面不加空格。

// 不推荐
var foo = 1,bar = 2
var foo = 1 , bar = 2
var foo = 1 ,bar = 2

// 推荐
var foo = 1, bar = 2

逗号请务必放在当前行

计算属性的空格

在对象的计算属性内,禁止使用空格

当属性名不带中划线等需要特别处理的字符串时,推荐直接使用obj.foo

// 不推荐
obj['foo' ]
obj[ 'foo']
obj[ 'foo' ]

// 推荐
obj['foo']

拖尾换行

在非空文件中,存在拖尾换行是一个常见的 UNIX 风格,它的好处是可以方便在串联和追加文件时不会打断 Shell 的提示。在日常的项目中,保留拖尾换行的好处是,可以减少版本控制时的代码冲突。

不推荐

function func () {
  // do something
}
推荐

function func () {
  // do something
}
  // 此处是新的一行

即每个文件的最后都包含一个空行

缩进

代码保持一致的缩进,是作为工程师的职业素养,也是减少合并冲突的保证
约定使用 空格 来缩进,而且缩进使用两个空格。

可通过设置IDE将tab设置为2空格

对象字面量的键值缩进

约定对象字面量的冒号和值之间存在一个空格

// 不推荐
var obj = { 'foo' : 'haha' }

// 推荐
var obj = { 'foo': 'haha' }

操作符的空格

约定操作符前后都需要添加空格

// 不推荐
var sum = 1+2

// 推荐
var sum = 1 + 2

构造函数首字母大写

约定构造函数的首字母要大写,以此来区分构造函数和普通函数。

类名大写也是一定的了~

// 不推荐
var fooItem = new foo()

// 推荐
var fooItem = new Foo()

空行

空白行对于分离代码逻辑有帮助,但过多的空行会占据屏幕的空间,影响可读性。

  • 约定最大连续空行数为 2
  • 约定函数内部首行不为空行
  • 约定函数间使用空行隔离

空格

  1. 除了每行的开头及末尾,代码块大括号两侧都应有空格

function foo () { return true } if (foo) {
  bar()
} else {
  baz()
}

  1. 操作符前后都添加空格

const sum = 1 + 2

  1. 逗号和冒号后面增加空格

const cat = { name: nico, color: 'white' }

  1. 行级注释// 前后添加空格

const a = 1; // 定义变量a

风格规范

好的编程风格是可以直接影响同事的感官的

注释

一定要写注释!
一定要写注释!
一定要写注释!

不管你用什么框架,使用什么ide,是否使用ts,请记住:

  • 文件、类、函数注释必须使用 JSDoc规范,使用/*内容/格式,不得使用 // xxx方式。
  • 方法内部单行注释,在被注释语句上方另起一行,使用//注释。
  • 所有的枚举类型字段必须要有注释,说明每个数据项的用途。
  • 与其“半吊子”英文来注释,不如用中文注释把问题说清楚
  • 好的命名、代码结构是自解释的,不使用无意义的注释

不管有多忙,请一定写上注释,否则过几个月连你自己都不知道自己怎么想的了。。。

/**
 * 处理所有关于数值计算的问题
 * 
 * @author XXX
 */
 
/**
 * 求和函数
 * @param { number } a 
 * @param { number} b
 * @returns {number} sum
 */
function sun(a, b) {
  return a + b;
}

抽取功能函数

  • 单一功能尽可能抽取功能函数
    • 页面加载时请求的不同类型数据的操作分别抽取成函数 -> 保证每个请求可以灵活修改逻辑
    • 页面销毁时需要设置的状态操作抽取成函数,-> 可以保证有统一的入口,防止遗漏某些操作
    • 控制组件状态的操作抽取成函数 -> 方便在流程中做拦截以及追加操作
  • 不要在框架任何一个生命周期函数中直接写大量语句 -> 这样真的会被揍
  • 把可能变化的地方封装成函数 -> 需求总是变化的,你懂的
  • 数据结构上要考虑功能将来的扩展 -> 同上

条件语句

  • 条件语句必须添加大括号
// bad
function test (opts) {
  if(opts.length > 0)
    // todo something
  // todo other
}


// good
function test (opts) {
  if(opts.length > 0) {
      // todo something
  }
  // todo other
}

定义工具类

定义自己的工具类, 尽量不要在业务代码里面直接调用第三方的工具类
如:

  • 字符串处理: 判断字符串是否符合规范
  • 判断是否是json对象

提示: 请写好注释,并告诉大家你的函数,否则 -> 跟没写差不多

谨慎注释掉代码。

  • 在上方详细说明,而不是简单地注释掉。
  • 废弃方法或废弃文件务必添加注释或删除

有时候会碰到改了半天代码结果bug毫无变化,然后惊喜的发现此方法已废弃。。。

最后,推荐方法三: ts/eslint开启上线前检查,不通过一律pass

请在新项目中使用,否则你会揍你自己


以上,酌情遵守,最重要的是大家开开心心的写代码就好

后续遇到新的想骂人的地方会补充上,请给我一键三连吧~~~

相关系列: 从零开始的前端筑基之旅(超级精细,持续更新~)

参考文档:
guide.aotu.io/docs/js/cod…
阿里巴巴Java开发手册