Airbnb JavaScript 风格指南-下篇

31 阅读7分钟

二十二、类型转换 Type Casting & Coercion

  • 22.1 在声明开头执行强制类型转换。Perform type coercion at the beginning of the statement.

  • 22.2 转为String:使用String(待转变量)将其他类型转为字符串。Strings:useString (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(); // isn’t guaranteed to return a string
    
    // good
    const totalScore = String(this.reviewScore);
    
  • 22.3 转为Number:使用Number 将其他类型转为Number,或使用parseInt 将String转为Number。Numbers: Use Number for type casting and parseInt always with a radix for parsing strings. (eslint: radix no-new-wrappers

    Why? The parseInt function produces an integer value dictated by interpretation of the contents of the string argument according to the specified radix. Leading whitespace in string is ignored. If radix is undefined or 0, it is assumed to be 10 except when the number begins with the character pairs 0x or 0X, in which case a radix of 16 is assumed. This differs from ECMAScript 3, which merely discouraged (but allowed) octal interpretation. Many implementations have not adopted this behavior as of 2013. And, because older browsers must be supported, always specify a 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);
    

  • 22.4 请在注释中解释为什么要用移位运算,无论你在做什么,比如由于 parseInt是你的性能瓶颈导致你一定要用移位运算。 请说明这个是因为性能原因。If for whatever reason you are doing something wild and parseInt is your bottleneck and need to use Bitshift for performance reasons, leave a comment explaining why and what you’re doing.

    // good
    /**
     * parseInt was the reason my code was slow.导致代码运行慢
    
     * Bitshifting the String to coerce it to a
     * Number made it a lot faster.
     */
    const val = inputValue >> 0;
    
  • 22.5 - 注意:  使用 bitshift 操作时要小心。数字表示为 64 位值,但 bitshift 操作始终返回 32 位整数。对于大于32位的整数值,Bitshift可能会导致意外行为。Note:  Be careful when using bitshift operations. Numbers are represented as 64-bit values, but bitshift operations always return a 32-bit integer (source). Bitshift can lead to unexpected behavior for integer values larger than 32 bits. Discussion. Largest signed 32-bit Int is 2,147,483,647:

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

  • 22.6 转为Boolean:使用Boolean()!!。Booleans: Use Boolean()||!! (eslint: no-new-wrappers

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

二十三、命名约定 Naming Conventions

  • 23.1 避免用一个字母命名,让你的命名更加语义化。Avoid single letter names. Be descriptive with your naming. (eslint: id-length)

    // bad
    function q() {
      // ...
    }
    
    // good
    function query() {
      // ...
    }
    
  • 23.2 用 驼峰 命名你的对象、函数、实例。Use camelCase when naming objects, functions, and instances. (eslint: camelcase

    // bad
    const OBJEcttsssss = {};
    const this_is_my_object = {};
    function c() {}
    
    // good
    const thisIsMyObject = {};
    function thisIsMyFunction() {}
    
  • 23.3 用 PascalCase 命名类。Use PascalCase only when naming constructors or classes. (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',
    });
    
  • 23.4 不要用前置或后置下划线。Do not use trailing or leading underscores. eslint: no-underscore-dangle

    Why? JavaScript does not have the concept of privacy in terms of properties or methods. Although a leading underscore is a common convention to mean “private”, in fact, these properties are fully public, and as such, are part of your public API contract. This convention might lead developers to wrongly think that a change won’t count as breaking, or that tests aren’t needed. tl;dr: if you want something to be “private”, it must not be observably present.

    // bad
    this.__firstName__ = 'Panda';
    this.firstName_ = 'Panda';
    this._firstName = 'Panda';
    
    // good
    this.firstName = 'Panda';
    
    // good, in environments where WeakMaps are available
    // see https://kangax.github.io/compat-table/es6/#test-WeakMap
    const firstNames = new WeakMap();
    firstNames.set(this, 'Panda');
    
  • 23.5 不要保存 this 的引用,使用箭头函数或硬绑定。Don’t save references to this. Use arrow functions or 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);
      };
    }
    
  • 23.6 文件名应与默认导出(export default)的名称完全匹配。A base filename should exactly match the name of its default export.

    // 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
    
  • 23.7 默认导出(export default)一个函数时,函数名、文件名统一。Use camelCase when you export-default a function. Your filename should be identical to your function’s name.

    function makeStyleGuide() {
      // ...
    }
    
    export default makeStyleGuide;
    
  • 23.8 当你 export 一个构造函数/类/单例/函数库对象时用 PascalCase。Use PascalCase when you export a constructor / class / singleton / function library / bare object.

    const AirbnbStyleGuide = {
      es6: {
      },
    };
    
    export default AirbnbStyleGuide;
    
  • 23.9 简称和首字母缩写应该全部大写或全部小写。Acronyms and initialisms should always be all uppercased, or all lowercased.

    Why? Names are for readability, not to appease a computer algorithm.

    // 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 = [
      // ...
    ];
    

  • 23.10 全大写字母定义用来导出的常量。You may optionally uppercase a constant only if it (1) is exported, (2) is a const (it can not be reassigned), and (3) the programmer can trust it (and its nested properties) to never change.

    Why? This is an additional tool to assist in situations where the programmer would be unsure if a variable might ever change. UPPERCASE_VARIABLES are letting the programmer know that they can trust the variable (and its properties) not to change.

    • What about all const variables? - This is unnecessary, so uppercasing should not be used for constants within a file. It should be used for exported constants however.
    • What about exported objects? - Uppercase at the top level of export (e.g. EXPORTED_OBJECT.key) and maintain that all nested properties do not change.
    // 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';
    
    // ---
    
    // allowed but does not supply semantic value
    export const apiKey = 'SOMEKEY';
    
    // better in most cases
    export const API_KEY = 'SOMEKEY';
    
    // ---
    
    // bad - unnecessarily uppercases key while adding no semantic value
    export const MAPPING = {
      KEY: 'value'
    };
    
    // good
    export const MAPPING = {
      key: 'value',
    };
    

二十四、访问器 Accessors

  • 24.1 不需要使用属性的访问器函数。Accessor functions for properties are not required.

  • 24.2 不要使用 JavaScript 的 getters/setters,因为他们会产生副作用,并且难以测试、维护和理解。如果必要,你可以用 getVal()和 setVal() 去构建。Do not use JavaScript getters/setters as they cause unexpected side effects and are harder to test, maintain, and reason about. Instead, if you do make accessor functions, use getVal() and setVal('hello').

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

  • 24.3 如果属性/方法是一个 boolean, 请用 isVal() 或 hasVal()。If the property/method is a boolean, use isVal() or hasVal().

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

  • 24.4 可以用 get() 和 set() 函数,但是要保持一致。It’s okay to create get() and set() functions, but be consistent.

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

二十九、标准库 Standard Library

标准库包含一些实用程序,这些实用程序在功能上已损坏,但由于遗留原因而保留。The Standard Library contains utilities that are functionally broken but remain for legacy reasons.

  • 29.1 使用 Number.isNaN 代替全局 isNaN。Use Number.isNaN instead of global isNaN. (eslint: no-restricted-globals

    Why? The global isNaN coerces non-numbers to numbers, returning true for anything that coerces to NaN. If this behavior is desired, make it explicit.

    为什么?全局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
    
  • 29.2 使用Number.isFinite代替全局isFinite。Use Number.isFinite instead of global isFinite. *(eslint: no-restricted-globals

  • Why? The global isFinite coerces non-numbers to numbers, returning true for anything that coerces to a finite number. If this behavior is desired, make it explicit.

    为什么?全局将非数字强制转换为数字,对于强制为有限数字的任何内容返回 true。 如果需要此行为,请明确说明。isFinite

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

三十、测试 Testing

  • 30.1 Yup.

    function foo() {
      return true;
    }
    
  • 30.2 No, but seriously:

    • Whichever testing framework you use, you should be writing tests!
    • Strive to write many small pure functions, and minimize where mutations occur.
    • Be cautious about stubs and mocks - they can make your tests more brittle.
    • We primarily use mocha and jest at Airbnb. tape is also used occasionally for small, separate modules.
    • 100% test coverage is a good goal to strive for, even if it’s not always practical to reach it.
    • Whenever you fix a bug, write a regression test. A bug fixed without a regression test is almost certainly going to break again in the future.