如果你还不熟悉 JavaScript 编写规则的话,谷歌发布了自己的 JS 编写风格指南,罗列出在他们看来最优雅的写法,以求写出的代码简单易读。

这份谷歌的风格指南并不提供硬性的要求,它不会教你如何写 JS,它只会针对如何写出连贯漂亮的 JS 提出风格方面的建议。JS 是很灵活的语言,允许很多编写风格的并存。

谷歌和爱彼邻都有发布自己的 JS 风格指南,在目前同类指南中是最受瞩目的两份。如果你工作中涉及到大量的 JS 编写,我强烈建议你花点时间研究一下这两份指南。

我从谷歌的风格指南中提取了 13 点在我看来最有意思且最有实际意义的建议,罗列如下,仅供参考。

无论是行内争议较大的一些问题,比如,缩进是用制表符还是用空格,如何使用分号,还是比较细枝末节的方面,谷歌 JS 风格指南都给出了自己很明确的回应。毋庸置疑,这份指南大大改变我写 JS 的习惯。

针对每一点,我都会先大致总结一下规则,然后附上指南的原文节选,来辅助对规则细节的理解。如果可能的话,我还会加上符合该条规则的代码范例来阐述,以及不合规的代码反例来加以对比。

缩进用空格,不要用制表符

除了行终止符,ASCII 水平空格字符(0x20)是源文件中出现过的唯一代表空白的字符,说明制表符不能用来缩进。

指南还推荐使用两个(不是四个)空格来完成缩进。

// bad
function foo() {
∙∙∙∙let name;
}

// bad
function bar() {
∙let name;
}

// good
function baz() {
∙∙let name;
}

必须用分号

每个语句都要用分号来结尾,禁止依靠自动分号插入。

我是无法理解为什么有人要质疑分号结尾的必要性,但就像缩进是用制表符还是空格,要不要在语句结尾加上分号也变得越来越有争议性。在这份指南中,谷歌给出了非常明确的回应,即必须使用分号。

// bad
let luke = {}
let leia = {}
[luke, leia].forEach(jedi => jedi.father = 'vader')
// good
let luke = {};
let leia = {};
[luke, leia].forEach((jedi) => {
  jedi.father = 'vader';
});

暂时还先别使用 ES6 的模块(modules)

暂时还先别用 ES6 的模块(例如  exportimport 命令),它们的语义还没完全确定下来。在 ES6 模块语义和用法完全确定下来后,我们会重新考量此条规则。

// 暂时先别这么写:
//------ lib.js ------
export function square(x) {
    return x * x;
}
export function diag(x, y) {
    return sqrt(square(x) + square(y));
}

//------ main.js ------
import { square, diag } from 'lib';

不鼓励(不禁止)水平对齐

虽然水平对齐并没有被禁用,但一般来说,谷歌是不鼓励这种做法的。在代码中如果已经做过水平对齐处理,谷歌也不鼓励保持水平对齐的做法。

在代码中加入多余的空格,以和上下行的同类变量保持对齐的做法就是所说的"水平对齐“。

// bad
{
  tiny:   42,  
  longer: 435, 
};
// good
{
  tiny: 42, 
  longer: 435,
};

别再用 var

所有的本地变量都应该使用 const 或者 let 来引出声明。除非该变量需要被重新赋值,不然都默认用 const 来声明。禁止再使用 var

在 StackOverflow 和其他地方,我还是会看到 var 出现在各种代码范例中,不知道是因为积习太难改,还是因为有人坚持认为 var 还有存在的理由……

// bad
var example = 42;
// good
let example = 42;

箭头函数是首选

箭头函数的句法更简洁,也解决了 this 关键词使用中的一些老大难问题。特别是在写嵌套函数的时候,箭头函数的优势相较传统的 function 表达式更突出,是首选。

我个人更喜欢箭头函数只是因为觉得它更简洁、更美观,碰巧写箭头函数确实能解决一些重要的实际问题。

// bad
[1, 2, 3].map(function (x) {
  const y = x + 1;
  return x * y;
});

// good
[1, 2, 3].map((x) => {
  const y = x + 1;
  return x * y;
});

使用模版字符串,而不要将普通字符串叠加

在处理复杂的字符串叠加,尤其是多行字符串的叠加时,模版字符串(使用反引号`来界定)是更优的选择,可以实现跨多行的效果。

// bad
function sayHi(name) {
  return 'How are you, ' + name + '?';
}

// bad
function sayHi(name) {
  return ['How are you, ', name, '?'].join();
}

// bad
function sayHi(name) {
  return `How are you, ${ name }?`;
}

// good
function sayHi(name) {
  return `How are you, ${name}?`;
}

长字符串不要使用反斜杠来分隔每行

请不要在基本字符串或者模版字符串的每行后用反斜杠(/)表示结尾,虽然 ES5 不会对这样的操作报错,但这样做会让你在排查错误的时候很头疼,因为斜杠后的多余空格会报错,而且很难看出。

爱彼邻(Airbnb)和谷歌在这一点上的推荐做法是有分歧的(详情参见爱彼邻的文档)。

谷歌建议用 + 运算符将多行字符串相连(如下所示),爱彼邻建议什么都不做,让长字符串自由覆盖。

// bad (sorry, this doesn't show up well on mobile)
const longString = 'This is a very long string that \
    far exceeds the 80 column limit. It unfortunately \
    contains long stretches of spaces due to how the \
    continued lines are indented.';
// good
const longString = 'This is a very long string that ' + 
    'far exceeds the 80 column limit. It does not contain ' + 
    'long stretches of spaces since the concatenated ' +
    'strings are cleaner.';

“for… of” 是写 for 循环的首选方式

在 ES6 体系下,JS 支持三种写 for 循环的方式,但 for-of 是最优选。

其实我也觉得这条规则有点奇怪,但是我还是选择把它纳入,因为考虑到谷歌会明确规定写 for 循环的最优方式,还是很有看头的。

我个人觉得,for…in 更适合对象的循环,for…of 更适合数组的循环,要视情况而定。

谷歌的建议也不一定和我的想法相悖,但针对这个问题谷歌有明确的表态,让我觉得有必要列出来思考一下。

别使用eval()

除了在代码加载器中,平时不要使用 eval,也不要使用 Function(...string) 构造函数,这些特性都是有潜在的危险的,而且在 CSP 环境中根本不起作用。

MDN上的 eval() 有一个章节的标题就是“避免使用 eval !"。

// bad
let obj = { a: 20, b: 30 };
let propName = getPropName();  // returns "a" or "b"
eval( 'var result = obj.' + propName );
// good
let obj = { a: 20, b: 30 };
let propName = getPropName();  // returns "a" or "b"
let result = obj[ propName ];  //  obj[ "a" ] is the same as obj.a

常量命名应该全大写,并且用下划线分隔单词

常量应该遵循 CONSTANT_CASE 命名规则:全大写,单词用下划线分割。

如果你确定一个变量的值不会发生改变,你应该用常量的命名规则来命名该变量,让你在浏览代码的时候更直观。

如果该常量仅定义并作用在某个函数内,那么这个常量的命名应该遵守 camelCase 规则,而不该全大写。

// bad
const number = 5;
// good
const NUMBER = 5;

每次只声明一个变量

每个声明语句里只包含一个变量,像 let a = 1, b = 2; 这样的写法是不合规、不推荐的。

// bad
let a = 1, b = 2, c = 3;
// good
let a = 1;
let b = 2;
let c = 3;

用单引号,不用双引号

普通字符串文字,用单引号 (')而不是双引号(")来标记字符串的结尾 。

小贴士:如果字符串中含有单引号,用模版字符串来避免单引号转义失效的情况。

// bad
let directive = "No identification of self or mission."
// bad
let saying = 'Say it ain\u0027t so.';
// good
let directive = 'No identification of self or mission.';
// good
let saying = `Say it ain't so`;

写在最后

这 13 点规则并不是所谓的的金规玉律,我在文章开头说过的,这里再强调一下。谷歌是科技巨擘之一,这些推荐处理方法只是他们的一家之言。

虽说如此,这一家之言是很有分量的,谷歌雇用了大量的顶尖工程师,资质深厚的他们写过大量的优质代码,他们的建议值得一看。

如果你想写出符合谷歌源文件级别标准的代码,你可以完全遵循谷歌这套风格指南中的所有规则。如果你对其中的个别规则或者所有规则有异议——很多人都有异议——那你大可不必理会这些建议。

我个人觉得在很多写法问题的建议处理上,爱彼邻的版本会比谷歌更合理一些。但万变不离其宗,无论你个人认为怎么样写才是优雅简洁的,在写代码的时候还是要遵循一定的规则,尽量做到前后风格一致。

原文链接:https://medium.freecodecamp.org/google-publishes-a-javascript-style-guide-here-are-some-key-lessons-1810b8ad050b,作者:Daniel Simmons