「译」 AirBnB 的 JavaScript 风格指南

没有人想写一些丑的、风格不连贯的代码,但是它们时有发生。

即使是项目上唯一的一个开发者,时间越久,你写的代码就越多,就会越来越难保持统一的代码风格。

这就是为啥你需要一个风格指南。

我亲身经历过多个团队如何通过使用风格指南来完成协作。

你不再需要整天电话确定风格,参照风格指南就好了。

当队友需要维护你的代码时,他们看到的都是可以看到阅读并理解的干净的代码。

使用风格指南是保证你代码可维护性最简单的方式之一,这也可以从根本上保证你团队的生产力。现在,我们来看看最流行的风格指南啦。

AirBnB + ESLint

JavaScript 的生态圈提供了丰富的工具和风格指南。没人惊奇这个。使用 JavaScript ,我们可以期待的是更丰富的东西。

随着生态圈的成熟,一些有经验的开发者开始向往「标准姿势」来进行标准化的开发工作。

花费几天时间来探索 JavaScript 生态圈并且比较不同的工具当然是很好的,但是我尽量节约你的时间:ESLint 是最流行的 JavaScript Lint 工具,AirBnB的风格指南是使用最广泛的风格指南.

更多细节你可以参照这里 把 ESLint 添加到你项目中去。

一旦你按那种方式使用,你可以通过在在的项目中安装对应的 npm 包来加强 AirBnB 的风格指南。

npm install --save-dev eslint-config-airbnb  

将下面这行添加到你的 .eslintrc文件中

"extends": "airbnb"

咦,你看。你的代码现在开始遵循着最流行的 JavaScript 风格指南了!码字愉快!

标准被高估

我赞成这份风格指南里面大部分的规定,这里是一份我覆写的配置文件。这就是你项目中的.eslint文件应该长的样子:

{
    "env": {
        "browser": true,
        "es6": true,
        "node": true,
        "mocha": true
    },
    "extends": "airbnb",
    "parser": "babel-eslint",
    "rules": {
        // indentation
        "indent": [ 2, 4 ],

        // spacing
        "space-in-parens": [ 2, "always" ],
        "template-curly-spacing": [ 2, "always" ],
        "array-bracket-spacing": [ 2, "always" ],
        "object-curly-spacing": [ 2, "always" ],
        "computed-property-spacing": [ 2, "always" ],
        "no-multiple-empty-lines": [ 2, { "max": 1, "maxEOF": 0, "maxBOF": 0 } ],

        // strings
        "quotes": [ 2, "double", "avoid-escape" ],

        // code arrangement matter
        "no-use-before-define": [ 2, { "functions": false } ],

        // make it meaningful
        "prefer-const": 1,

        // keep it simple
        "complexity": [ 1, 5 ],

        // react
        "react/prefer-es6-class": 0,
        "react/jsx-filename-extension": 0,
        "react/jsx-curly-spacing": [ 2, "always" ],
        "react/jsx-indent": [ 2, 4 ]
    }
}

来让我们看看着每行配置背后的含义。

缩进

tabs VS spaces,会破坏友谊,也可能会毁灭一段浪漫的关系。(详见硅谷)

我倾向于使用 4 个空格来缩进,即使绝大多数的配置都倾向于使用 2 个空格缩进。

我的理由是,为了写出干净的代码,大的缩进可以给你一个更好的视野来判断你在你的函数中嵌套了多少层,或者是你使用了多少中不同的括号。

你绝对不应该在一个 JavaScript 文件中将代码的嵌套深层弄到 3、4 层(这就是代码的味道)。所以,使用 4 个空格的缩进可以给你更好的阅读性,同时保留其他的规则,比如保持你的每行代码在 80 或者是 120 个字符长以内。

缩进是可以让你更好的品味 AirBnB 默认风格指南的方式之一。结果是,你的代码不会全挤在编辑器的左边过于难看。

空格(Spacing)

这可能是背离标准最大的一块。我讨厌拥挤的代码,我早在 2 年前就开始在代码中留出额外的空格,而且也从来没有想过不这样做。

这些规则到底是啥意思呢?好,来看看下面的代码。它代表了 AirBnB 的官方风格指南。

function async({ param }, actions, connection = null) {  
  return new Promise((resolve, reject) => {
    const path = param.split("?")[0];
    curriedFunction(actions.getNode)(path, connection);

    const store = StoreFactory.get(connection);
    const unsubscribe = store.listen(() => {
      const node = store.getNode();
      const extraParams = Helpers.getParams(node, connection);
      const finalNode = Object.assign({}, node, extraParams);
      curriedFunction(actions.getTag)({ finalNode, param }).then(resolve);
      unsubscribe();
    });
  });
}

我知道,这有点混乱。我尝试从我的代码库中找一个中等复杂的函数,但是这样我就说不清楚这个东西了,所以不要尝试去理解这个代码中的逻辑(因为没有逻辑)。只需要尝试阅读就可以了。比如,尝试聚焦在使用在多个地方的变量,当变量传递给函数,同时函数调用的地方。

现在,看看同一段代码,但是使用了额外的空格规则:

function async( { param }, actions, connection = null ) {  
    return new Promise( ( resolve, reject ) => {
        const path = param.split( "?" )[ 0 ];
        curriedFunction( actions.getNode )( path, connection );

        const store = StoreFactory.get( connection );
        const unsubscribe = store.listen( ( ) => {
            const node = store.getNode( );
            const extraParams = Helpers.getParams( node, connection );
            const finalNode = Object.assign( { }, node, extraParams );
            curriedFunction( actions.getTag )( { finalNode, param } ).then( resolve );
            unsubscribe( );
        } );
    } );
}

我不会说这段代码的可读性非常强,但是你可以非常清楚的看到你的变量是在哪里传递给函数的,尤其被柯里化函数包围的时候。

在这两个例子中,同时可以比较一下 2 格和 4 格缩进的不同之处。

一开始,这些技巧看起来并不是一个大的进步。我鼓励你去尝试,我觉得你会快速的发现这些区别带来的好处。

引用之战

尽管前两个类别有一些清晰的参数,但是我必须说的是,单引号 VS 双引号的使用是一件需要保持高度一致的事情。

我倾向于使用双引号大概是因为使用 React 工作的时间比较久,因为你需要把 JavaScript 混到 JSX 标签中。由于 JSX 的语法和 HTML很像,所以倾向于使用双引号将需要添加的属性包裹起来。所以我开始使用双引号来应对所有场景,以保持一致性。

我注意到一件事情,那就是你更加可能需要的是在一个英文语句中使用单引号而不是双引号(“I’m here”, “Don’t do that”)。所以,双引号在这些情况更加适用。

代码布置

AirBnB 官方指南中有一条规定「不在定义之前使用」,如果你尝试在定义之前使用就是抛出一个错误。这是一条好规定 -- 尤其对于某些变量 -- 因为你不应该依赖(函数声明)提升,你需要的是在使用 letconst的时候,将临时的死区(temporal dead zone),问题时刻放在头脑中。

我倾向于允许在定义之前使用函数。原因很简单:大多数情况下,「不在定义之前使用」规定会让你把次函数(sub-function)定义在主函数(original-function)之前。

但是你的次函数只是逻辑里面抽象的模块,它们不应该影响你打开一个包含最高层级函数 -- 你会从外部引用 -- 的文件。

当然啦,这是值得争论的事情。但是这会影响到 debug !当你迭代某些代码,你发现了一个调用使用多个函数,你自然会看看下面都干了些啥,或者糟糕一点的场景下,你需要往下翻几个次函数(sub-function)来找找你需要的东西。

这里,有一份整合了 AirBnB 的函数声明和函数表达式的规定,可以让你顺着你的性子 、自由地构建模块或者函数库,而不会一不小心的调用了一个没有初始化的函数。

Const 优于 Let

这里和风格指南有另外一个小的背离,看看我的配置文件:

"prefer-const": 1

在 AirBnB 的配置中,这里被设置成 2 ,代表错误(error),如果设置成 1 ,就代表这 警告(warning)。

现在,如果你明白为啥你应该优先使用 const 而不是 let,你可以从这里这里

在我的背离中,我倾向于使用及警告,因为有一些场景下,你并不会改变变量的定义,但是你会改变变量的内容。

看看代码:

const hash = { };  
const pairs = [  
    { key: "a", value: 1 }, 
    { key: "b", value: 2 }, 
    { key: "c", value: 3 },
];

pairs.forEach( pair => hash[ pair.key ] = pair.value );  

这条规定会告诉你这样是对的 - hash 应该是 const的,因为他们不会被重定义。但是这从来不会对我造成任何影响,我只需要知道,是hash里面(不是别的地方)会有巨大的变化。

所以,我会使用 let 来标明(嗯,表明也是可以的)这个变量会改变。缘于 constlet 包含了你的变量的一些信息,任何情况下都不要将真相隐藏。

代码的复杂性

你可以 cyclomatic code complexity来计算你每个函数的复杂度。

设置最大层级的复杂度是搞笑的,理论上来说,可能性很小,意味着你的函数最多只能有 1 或者 2 一个括号。

有意义的是,从代码复用和测试的角度考虑,使那个数字尽可能的低。但是有的时候确实很难将函数降级。这就是为啥我把复杂度看成警告而不是错误。

重要的事情是将函数的那个数字做限制,尽量不达到最高的复杂度。即使一个中型的代码库,我也不会想看到有 10 个函数违背了这个规定。所以为了标出(高亮)这些函数,我对于这个数字的选择是 5。我会跟进这些函数,并在后来进行重构。

复杂度可以从很多方面来解决。最明显的方式是重构。另外一个选择就是将你的switch语句改成 mapping定义。

我们团队有一些争论,最后我们使用 5 为一个参考值来作为最大的复杂度停止了争论。但是有些情况下,我们使用 4 ,只是为了确定我们的代码干净且简单。

一个 React 的笔记

以为我使用 React 工作,AirBnB 的配置也包含了非常多的相关规定,我也希望写一些关于这方面的偏好。

我的 React 方面的覆写主要目的是对常规 JavaScript 模块和 React 组件之间的差异进行限制,也就是 JavaScript 和JSX 之间。这也是为啥我选择类似的缩进和对所有的 JSX 属性使用双引号。也是为啥我对所有的文件都使用 .js做扩展名。

最后,关于Class 和 Factory 组件,我倾向于后者。我看不到使用 Classes 的任何好处。我可能会在就使用进行一篇展开说明。

这就是所有啦!我希望你读的开心。随时欢迎你的反馈。

如果你喜欢这篇文章,就来给我点赞吧,这样我就知道我的努力没有白费!

原文链接:https://medium.freecodecamp.com/adding-some-air-to-the-airbnb-style-guide-3df40e31c57a#.nu7mmp3bw

作者:Alex Moldovan; Medium:https://medium.freecodecamp.com/@alexnm

吴瑞诚:斗鱼实时计算平台的演进 →
← 「译」 你可能不需要 react-router