Airbnb JavaScript 风格指南2

https://github.com/airbnb/javascript#translation

目录

  1. Semicolons
  2. Type Casting & Coercion
  3. Naming Conventions
  4. Accessors
  5. Events
  6. jQuery
  7. ECMAScript 5 Compatibility
  8. ECMAScript 6+ (ES 2015+) Styles
  9. Standard Library
  10. Testing
  11. Performance
  12. Resources
  13. In the Wild
  14. Translation
  15. The JavaScript Style Guide Guide
  16. Chat With Us About JavaScript
  17. Contributors
  18. License
  19. Amendments

Semicolons

<a name="21.1"></a>

  • 21.1 Yup. eslint: semi

    Why? 当 JavaScript 遇到没有分号结尾的一行,它会执行自动插入分号 Automatic Semicolon Insertion这一规则来决定行末是否加分号。如果JavaScript在你的断行里错误的插入了分号,就会出现一些古怪的行为。当新的功能加到JavaScript里后, 这些规则会变得更复杂难懂。显示的结束语句,并通过配置代码检查去捕获没有带分号的地方可以帮助你防止这种错误。

    // bad
    (function () {
      const name = 'Skywalker'
      return name
    })()
    
    // good
    (function () {
      const name = 'Skywalker';
      return name;
    }());
    
    // good, 行首加分号,避免文件被连接到一起时立即执行函数被当做变量来执行。
    ;(() => {
      const name = 'Skywalker';
      return name;
    }());
    

    Read more.

⬆ back to top

Type Casting & Coercion

<a name="22.1"></a>
<a name="coercion--explicit"></a>

  • 22.1 在语句开始执行强制类型转换。

<a name="22.2"></a>
<a name="coercion--strings"></a>

  • 22.2 Strings: eslint: no-new-wrappers

    // => this.reviewScore = 9;
    
    // bad
    const totalScore = new String(this.reviewScore); // typeof totalScore is "object" not "string"
    
    // bad
    const totalScore = this.reviewScore + ''; // invokes this.reviewScore.valueOf()
    
    // bad
    const totalScore = this.reviewScore.toString(); // 不保证返回string
    
    // good
    const totalScore = String(this.reviewScore);
    

<a name="22.3"></a>
<a name="coercion--numbers"></a>

  • 22.3 Numbers: 用 Number 做类型转换,parseInt转换string常需要带上基数。 eslint: radix

    const inputValue = '4';
    
    // bad
    const val = new Number(inputValue);
    
    // bad
    const val = +inputValue;
    
    // bad
    const val = inputValue >> 0;
    
    // bad
    const val = parseInt(inputValue);
    
    // good
    const val = Number(inputValue);
    
    // good
    const val = parseInt(inputValue, 10);
    

<a name="22.4"></a>
<a name="coercion--comment-deviations"></a>

  • 22.4 请在注释中解释为什么要用移位运算和你在做什么。无论你做什么狂野的事,比如由于 parseInt 是你的性能瓶颈导致你一定要用移位运算。 请说明这个是因为性能原因,

    // good
    /**
     * parseInt是代码运行慢的原因
     * 用Bitshifting将字符串转成数字使代码运行效率大幅增长
     */
    const val = inputValue >> 0;
    

<a name="22.5"></a>
<a name="coercion--bitwise"></a>

  • 22.5 注意: 用移位运算要小心. 数字使用64-位表示的,但移位运算常常返回的是32为整形source)。移位运算对大于32位的整数会导致意外行为。Discussion. 最大的32位整数是 2,147,483,647:

    2147483647 >> 0 //=> 2147483647
    2147483648 >> 0 //=> -2147483648
    2147483649 >> 0 //=> -2147483647
    

<a name="22.6"></a>
<a name="coercion--booleans"></a>

  • 22.6 布尔:

    const age = 0;
    
    // bad
    const hasAge = new Boolean(age);
    
    // good
    const hasAge = Boolean(age);
    
    // best
    const hasAge = !!age;
    

⬆ back to top

Naming Conventions

<a name="23.1"></a>
<a name="naming--descriptive"></a>

  • 23.1 避免用一个字母命名,让你的命名可描述。 eslint: id-length

    // bad
    function q() {
      // ...
    }
    
    // good
    function query() {
      // ...
    }
    

<a name="23.2"></a>
<a name="naming--camelCase"></a>

  • 23.2 用小驼峰式命名你的对象、函数、实例。 eslint: camelcase

    // bad
    const OBJEcttsssss = {};
    const this_is_my_object = {};
    function c() {}
    
    // good
    const thisIsMyObject = {};
    function thisIsMyFunction() {}
    

<a name="23.3"></a>
<a name="naming--PascalCase"></a>

  • 23.3 用大驼峰式命名类。 eslint: new-cap

    // bad
    function user(options) {
      this.name = options.name;
    }
    
    const bad = new user({
      name: 'nope',
    });
    
    // good
    class User {
      constructor(options) {
        this.name = options.name;
      }
    }
    
    const good = new User({
      name: 'yup',
    });
    

<a name="23.4"></a>
<a name="naming--leading-underscore"></a>

  • 23.4 不要用前置或后置下划线。 eslint: no-underscore-dangle

    Why? JavaScript 没有私有属性或私有方法的概念。尽管前置下划线通常的概念上意味着“private”,事实上,这些属性是完全公有的,因此这部分也是你的API的内容。这一概念可能会导致开发者误以为更改这个不会导致崩溃或者不需要测试。 如果你想要什么东西变成“private”,那就不要让它在这里出现。

    // bad
    this.__firstName__ = 'Panda';
    this.firstName_ = 'Panda';
    this._firstName = 'Panda';
    
    // good
    this.firstName = 'Panda';
    

<a name="23.5"></a>
<a name="naming--self-this"></a>

  • 23.5 不要保存引用this, 用箭头函数或函数绑定——Function#bind.

    // bad
    function foo() {
      const self = this;
      return function () {
        console.log(self);
      };
    }
    
    // bad
    function foo() {
      const that = this;
      return function () {
        console.log(that);
      };
    }
    
    // good
    function foo() {
      return () => {
        console.log(this);
      };
    }
    

<a name="23.6"></a>
<a name="naming--filename-matches-export"></a>

  • 23.6 export default导出模块A,则这个文件名也叫A.*, import 时候的参数也叫A。 大小写完全一致。

    // file 1 contents
    class CheckBox {
      // ...
    }
    export default CheckBox;
    
    // file 2 contents
    export default function fortyTwo() { return 42; }
    
    // file 3 contents
    export default function insideDirectory() {}
    
    // in some other file
    // bad
    import CheckBox from './checkBox'; // PascalCase import/export, camelCase filename
    import FortyTwo from './FortyTwo'; // PascalCase import/filename, camelCase export
    import InsideDirectory from './InsideDirectory'; // PascalCase import/filename, camelCase export
    
    // bad
    import CheckBox from './check_box'; // PascalCase import/export, snake_case filename
    import forty_two from './forty_two'; // snake_case import/filename, camelCase export
    import inside_directory from './inside_directory'; // snake_case import, camelCase export
    import index from './inside_directory/index'; // requiring the index file explicitly
    import insideDirectory from './insideDirectory/index'; // requiring the index file explicitly
    
    // good
    import CheckBox from './CheckBox'; // PascalCase export/import/filename
    import fortyTwo from './fortyTwo'; // camelCase export/import/filename
    import insideDirectory from './insideDirectory'; // camelCase export/import/directory name/implicit "index"
    // ^ supports both insideDirectory.js and insideDirectory/index.js
    

<a name="23.7"></a>
<a name="naming--camelCase-default-export"></a>

  • 23.7 当你export-default一个函数时,函数名用小驼峰,文件名需要和函数名一致。

    function makeStyleGuide() {
      // ...
    }
    
    export default makeStyleGuide;
    

<a name="23.8"></a>
<a name="naming--PascalCase-singleton"></a>

  • 23.8 当你export一个结构体/类/单例/函数库/对象 时用大驼峰。

    const AirbnbStyleGuide = {
      es6: {
      }
    };
    
    export default AirbnbStyleGuide;
    

<a name="23.9"></a>
<a name="naming--Acronyms-and-Initialisms"></a>

  • 23.9 简称和缩写应该全部大写或全部小写。

    Why? 名字都是给人读的,不是为了适应电脑的算法的。

    // bad
    import SmsContainer from './containers/SmsContainer';
    
    // bad
    const HttpRequests = [
      // ...
    ];
    
    // good
    import SMSContainer from './containers/SMSContainer';
    
    // good
    const HTTPRequests = [
      // ...
    ];
    
    // also good
    const httpRequests = [
      // ...
    ];
    
    // best
    import TextMessageContainer from './containers/TextMessageContainer';
    
    // best
    const requests = [
      // ...
    ];
    

<a name="23.10"></a>
<a name="naming--uppercase"></a>

  • 23.10 你可以用全大写字母设置静态变量,他需要满足三个条件。

    1. 导出变量
    2. const 定义的, 保证不能被改变
    3. 这个变量是可信的,他的子属性都是不能被改变的

    Why? 这是一个附加工具,帮助开发者去辨识一个变量是不是不可变的。
    - 对于所有的 const 变量呢? —— 这个是不必要的。大写变量不应该在同一个文件里定义并使用, 它只能用来作为导出变量。 赞同!
    - 那导出的对象呢? —— 大写变量处在export的最高级(e.g. EXPORTED_OBJECT.key) 并且他包含的所有子属性都是不可变的。

    // bad
    const PRIVATE_VARIABLE = 'should not be unnecessarily uppercased within a file';
    
    // bad
    export const THING_TO_BE_CHANGED = 'should obviously not be uppercased';
    
    // bad
    export let REASSIGNABLE_VARIABLE = 'do not use let with uppercase variables';
    
    // ---
    
    // 允许但不够语义化
    export const apiKey = 'SOMEKEY';
    
    // 在大多数情况下更好
    export const API_KEY = 'SOMEKEY';
    
    // ---
    
    // bad - 不必要的大写键,没有增加任何语言
    export const MAPPING = {
      KEY: 'value'
    };
    
    // good
    export const MAPPING = {
      key: 'value'
    };
    

⬆ back to top

Accessors

<a name="24.1"></a>
<a name="accessors--not-required"></a>

  • 24.1 不需要使用属性的访问器函数。

<a name="24.2"></a>
<a name="accessors--no-getters-setters"></a>

  • 24.2 不要使用JavaScript的getters/setters,因为他们会产生副作用,并且难以测试、维护和理解。相反的,你可以用 getVal()和setVal('hello')去创造你自己的accessor函数

    // bad
    class Dragon {
      get age() {
        // ...
      }
    
      set age(value) {
        // ...
      }
    }
    
    // good
    class Dragon {
      getAge() {
        // ...
      }
    
      setAge(value) {
        // ...
      }
    }
    

<a name="24.3"></a>
<a name="accessors--boolean-prefix"></a>

  • 24.3 如果属性/方法是boolean, 用 isVal()hasVal()

    // bad
    if (!dragon.age()) {
      return false;
    }
    
    // good
    if (!dragon.hasAge()) {
      return false;
    }
    

<a name="24.4"></a>
<a name="accessors--consistent"></a>

  • 24.4 用get()和set()函数是可以的,但是要一起用

    class Jedi {
      constructor(options = {}) {
        const lightsaber = options.lightsaber || 'blue';
        this.set('lightsaber', lightsaber);
      }
    
      set(key, val) {
        this[key] = val;
      }
    
      get(key) {
        return this[key];
      }
    }
    

⬆ back to top

Events

<a name="25.1"></a>
<a name="events--hash"></a>

  • 25.1 通过哈希而不是原始值向事件装载数据时(不论是DOM事件还是像Backbone事件的很多属性)。 这使得后续的贡献者(程序员)向这个事件装载更多的数据时不用去找或者更新每个处理器。例如:

    // bad
    $(this).trigger('listingUpdated', listing.id);
    
    // ...
    
    $(this).on('listingUpdated', (e, listingID) => {
      // do something with listingID
    });
    

    prefer:

    // good
    $(this).trigger('listingUpdated', { listingID: listing.id });
    
    // ...
    
    $(this).on('listingUpdated', (e, data) => {
      // do something with data.listingID
    });
    

⬆ back to top

jQuery

<a name="26.1"></a>
<a name="jquery--dollar-prefix"></a>

  • 26.1 jQuery对象用$变量表示。

    // bad
    const sidebar = $('.sidebar');
    
    // good
    const $sidebar = $('.sidebar');
    
    // good
    const $sidebarBtn = $('.sidebar-btn');
    

<a name="26.2"></a>
<a name="jquery--cache"></a>

  • 26.2 暂存jQuery查找

    // bad
    function setSidebar() {
      $('.sidebar').hide();
    
      // ...
    
      $('.sidebar').css({
        'background-color': 'pink'
      });
    }
    
    // good
    function setSidebar() {
      const $sidebar = $('.sidebar');
      $sidebar.hide();
    
      // ...
    
      $sidebar.css({
        'background-color': 'pink'
      });
    }
    

<a name="26.3"></a>
<a name="jquery--queries"></a>

  • 26.3 DOM查找用层叠式$('.sidebar ul') 或 父节点 > 子节点 $('.sidebar > ul'). jsPerf

<a name="26.4"></a>
<a name="jquery--find"></a>

  • 26.4 用jQuery对象查询作用域的find方法查询

    // bad
    $('ul', '.sidebar').hide();
    
    // bad
    $('.sidebar').find('ul').hide();
    
    // good
    $('.sidebar ul').hide();
    
    // good
    $('.sidebar > ul').hide();
    
    // good
    $sidebar.find('ul').hide();
    

⬆ back to top

ES5 兼容性

<a name="27.1"></a>
<a name="es5-compat--kangax"></a>

⬆ back to top

ECMAScript 6+ (ES 2015+) Styles

<a name="28.1"></a>
<a name="es6-styles"></a>

  • 28.1 这是收集到的各种ES6特性的链接
  1. 箭头函数——Arrow Functions
  2. 类——Classes
  3. 对象缩写——Object Shorthand
  4. 对象简写——Object Concise
  5. 对象计算属性——Object Computed Properties
  6. 模板字符串——Template Strings
  7. 解构赋值——Destructuring
  8. 默认参数——Default Parameters
  9. Rest
  10. Array Spreads
  11. Let and Const
  12. 幂操作符——Exponentiation Operator
  13. 迭代器和生成器——Iterators and Generators
  14. 模块——Modules

<a name="28.2"></a>
<a name="tc39-proposals"></a>

  • 28.2 不要用TC39 proposals, TC39还没有到 stage 3。

    Why? 它还不是最终版, 他可能还有很多变化,或者被撤销。 我们想要用的是 JavaScript, 提议还不是JavaScript。

⬆ back to top

Standard Library

标准库中包含一些功能受损但是由于历史原因遗留的工具类

<a name="29.1"></a>
<a name="standard-library--isnan"></a>

  • 29.1Number.isNaN 代替全局的 isNaN.
    eslint: no-restricted-globals

    Why? 全局 isNaN 强制把非数字转成数字, 然后对于任何强转后为 NaN 的变量都返回 true
    如果你想用这个功能,就显式的用它。

    // bad
    isNaN('1.2'); // false
    isNaN('1.2.3'); // true
    
    // good
    Number.isNaN('1.2.3'); // false
    Number.isNaN(Number('1.2.3')); // true
    

<a name="29.2"></a>
<a name="standard-library--isfinite"></a>

  • 29.2Number.isFinite 代替 isFinite.
    eslint: no-restricted-globals

    Why? 理由同上,会把一个非数字变量强转成数字,然后做判断。

    // bad
    isFinite('2e3'); // true
    
    // good
    Number.isFinite('2e3'); // false
    Number.isFinite(parseInt('2e3', 10)); // true
    

Testing

<a name="30.1"></a>
<a name="testing--yup"></a>

  • 30.1 Yup.

    function foo() {
      return true;
    }
    

<a name="30.2"></a>
<a name="testing--for-real"></a>

  • 30.2 No, but seriously:
  • 无论用那个测试框架,你都需要写测试。
  • 尽量去写很多小而美的纯函数,减少突变的发生
  • 小心 stub 和 mock —— 这会让你的测试变得脆弱。
  • 在 Airbnb 首选 mochatape 偶尔被用来测试一些小的,独立的模块。
  • 100%测试覆盖率是我们努力的目标,即便实际上很少达到。
  • 每当你修了一个bug, 都要写一个回归测试。 一个bug修复了,没有回归测试,很可能以后会再次出问题。

⬆ back to top

Performance

⬆ back to top

Resources

Learning ES6

Read This

Tools

Other Style Guides

Other Styles

Further Reading

Books

Blogs

Podcasts

⬆ back to top

In the Wild

This is a list of organizations that are using this style guide. Send us a pull request and we'll add you to the list.

⬆ back to top

Translation

This style guide is also available in other languages:

The JavaScript Style Guide Guide

Chat With Us About JavaScript

Contributors

License

(The MIT License)

Copyright (c) 2012 Airbnb

Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
'Software'), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:

The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

⬆ back to top

Amendments

We encourage you to fork this guide and change the rules to fit your team’s style guide. Below, you may list some amendments to the style guide. This allows you to periodically update your style guide without having to deal with merge conflicts.

};

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 215,794评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,050评论 3 391
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 161,587评论 0 351
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,861评论 1 290
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,901评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,898评论 1 295
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,832评论 3 416
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,617评论 0 271
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,077评论 1 308
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,349评论 2 331
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,483评论 1 345
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,199评论 5 341
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,824评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,442评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,632评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,474评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,393评论 2 352