继续浏览精彩内容
慕课网APP
程序员的梦工厂
打开
继续
感谢您的支持,我会继续努力的
赞赏金额会直接到老师账户
将二维码发送给自己后长按识别
微信支付
支付宝支付

写更漂亮的javascript

www说
关注TA
已关注
手记 435
粉丝 83
获赞 493

用更合理的方式写 JavaScript

<a name="table-of-contents"></a>

目录

  1. 声明变量

  2. 对象

  3. 数组

  4. 字符串

  5. 函数

  6. 箭头函数

  7. 模块

  8. 迭代器和生成器

  9. 属性

  10. 变量

  11. 提升

  12. 比较运算符和等号

  13. 代码块

  14. 注释

  15. 空白

  16. 逗号

  17. 分号

  18. 类型转换

  19. 命名规则

<a name="types"></a>

声明变量

  • 1.1 <a name='1.1'></a> 使用let和const代替var

    //bad var $cat = $('.cat')//goodconst $cat = $('.cat')
    //bad var count = 1if (count<10) {
        count += 1}//goodlet count = 1if (count<10) {
        count += 1}
    • 会变动的声明用let

    • 不会变的声明用const

  • 1.2 <a name='1.2'></a> 将所有的 constlet 分组

    为什么?当你需要把已赋值变量赋值给未赋值变量时非常有用。

    // badlet i, len, dragonball,
        items = getItems(),
        goSportsTeam = true;// badlet i;const items = getItems();let dragonball;const goSportsTeam = true;let len;// goodconst goSportsTeam = true;const items = getItems();let dragonball;let i;let length;
  • 1.3 <a name='1.3'></a> 在你需要的地方给变量赋值,但请把它们放在一个合理的位置。

    为什么?letconst 是块级作用域而不是函数作用域。

    // bad - unnecessary function callconst checkName = function (hasName) {    const name = getName();    if (hasName === 'test') {        return false;
        }    if (name === 'test') {        this.setName('');        return false;
        }    return name;
    };// goodconst checkName = function (hasName) {    if (hasName === 'test') {        return false;
        }    const name = getName();    if (name === 'test') {        this.setName('');        return false;
        }    return name;
    };

返回目录

<a name="objects"></a>

对象 Objects

  • 2.1 <a name='2.1'></a> 使用字面值创建对象。eslint: no-new-object

    // badconst item = new Object();// goodconst item = {};

<a name="es6-computed-properties"></a>

  • 2.2 <a name='2.2'></a> 创建有动态属性名的对象时,使用可被计算的属性名称。

    为什么?因为这样可以让你在一个地方定义所有的对象属性。

    const getKey = function (k) {    return `a key named ${k}`;
    };// badconst obj = {    id: 1,    name: 'San Francisco',
    };
    obj[getKey('enabled')] = true;// goodconst obj = {    id: 1,    name: 'San Francisco',
        [getKey('enabled')]: true,
    };

<a name="es6-object-shorthand"></a>

  • 2.3 <a name='2.3'></a> 使用对象方法的简写。eslint: object-shorthand

    // badconst atom = {    value: 1,    addValue: function (value) {        return atom.value + value;
        },
    };// goodconst atom = {    value: 1,
    
        addValue(value) {        return atom.value + value;
        },
    };

<a name="es6-object-concise"></a>

  • 2.4 <a name='2.4'></a> 使用对象属性值的简写。eslint: object-shorthand

    为什么?因为这样更短更有描述性。

    const lukeSkywalker = 'Luke Skywalker';// badconst obj = {    lukeSkywalker: lukeSkywalker,
    };// goodconst obj = {
        lukeSkywalker,
    };
  • 2.5 <a name='2.5'></a> 在对象属性声明前把简写的属性分组。

    为什么?因为这样能清楚地看出哪些属性使用了简写。

    const anakinSkywalker = 'Anakin Skywalker';const lukeSkywalker = 'Luke Skywalker';// badconst obj = {    episodeOne: 1,    twoJedisWalkIntoACantina: 2,
        lukeSkywalker,    episodeThree: 3,    mayTheFourth: 4,
        anakinSkywalker,
    };// goodconst obj = {
        lukeSkywalker,
        anakinSkywalker,    episodeOne: 1,    twoJedisWalkIntoACantina: 2,    episodeThree: 3,    mayTheFourth: 4,
    };
  • 2.6 <a name='2.6'></a> ==只==把对象中是无效标识符的属性用引号括起来。 eslint: quote-props

    为什么?通常认为这样写更容易阅读,更好的支持语法高亮,也更容易被许多 JS 引擎优化。

    // badconst bad = {    'foo': 3,    'bar': 4,    'data-blah': 5,
    };// goodconst good = {    foo: 3,    bar: 4,    'data-blah': 5,
    };
  • 2.7 <a name='2.7'></a> 不要直接调用 Object.prototype 方法,比如 hasOwnProperty, propertyIsEnumerable,和 isPrototypeOf

    为什么?这些方法有可能被对象本身的方法所遮蔽 - 比如{ hasOwnProperty: false } - 或者, 这个对象是个 null object (Object.create(null))。

    // badconsole.log(object1.hasOwnProperty(key));// goodconsole.log(Object.prototype.hasOwnProperty.call(object, key));// bestconst has = Object.prototype.hasOwnProperty; // cache the lookup once, in module scope./* or */import has from 'has'; // https://www.npmjs.com/package/has// ...console.log(has.call(object, key));
  • 2.8 <a name='2.8'></a> 使用对象字面量创建对象时,大括号必须换行。使用对象解构赋值时,如果对象属性超过2个,必须换行。eslint: object-curly-newline

    // badconst original = {a: 1, b: 2};const {height, largeSize, smallSize} = item;// goodconst original = {    a: 1, 
        b: 2,
    };const {
        height,
        largeSize,
        smallSize,
    } = item;const {height, largeSize} = item;
  • 2.9 <a name='2.9'></a>
    单行定义对象时,最后一个成员不以逗号结尾。多行定义的对象,最后一个成员以逗号结尾。

    // badconst foo = { k1: v1, k2: v2, };const bar = {    k1: v1,    k2: v2
    };// goodconst foo = { k1: v1, k2: v2 };const bar = {    k1: v1,    k2: v2,
    };

返回目录

<a name="arrays"></a>

数组 Arrays

  • 3.1 <a name='3.1'></a> 使用字面值创建数组。eslint: no-array-constructor

    // badconst items = new Array();// goodconst items = [];
  • 3.2 <a name='3.2'></a> 向数组添加元素时使用 Array#push 替代直接赋值。

    const someStack = [];// badsomeStack[someStack.length] = 'abracadabra';// goodsomeStack.push('abracadabra');

<a name="es6-array-spreads"></a>

  • 3.3 <a name='3.3'></a> 使用扩展运算符 ... 复制数组。

    // badconst len = items.length;const itemsCopy = [];let i;for (i = 0; i < len; i++) {
        itemsCopy[i] = items[i];
    }// goodconst itemsCopy = [...items];
  • 3.4 <a name='3.4'></a> 使用扩展运算符 ... 代替 Array.from 把一个类数组对象转换成数组。

    const foo = document.querySelectorAll('.foo');// goodconst nodes = Array.from(foo);// bestconst nodes = [...foo];
  • 3.5 <a name='3.5'></a> 使用 Array.from 代替扩展运算符 ... 来映射迭代器,因为这样可以避免创建一个中间数组。(array.from可以把类数组或者字符串转化为数组)

    // badconst baz = [...foo].map(bar);// goodconst baz = Array.from(foo, bar);
  • 3.6 <a name='3.6'></a> 在数组方法的回调中使用return语句。如果函数只有一行代码,并且只返回了一个表达式,那么可以省略返回值。关联 7.2. eslint: array-callback-return

    // good[1, 2, 3].map((x) => {    const y = x + 1;    return x * y;
    });// good[1, 2, 3].map(x => x + 1);// bad - no returned value means `memo` becomes undefined after the first iteration[[0, 1], [2, 3], [4, 5]].reduce((memo, item, index) => {    const flatten = memo.concat(item);
        memo[index] = flatten;
    });// good[[0, 1], [2, 3], [4, 5]].reduce((memo, item, index) => {    const flatten = memo.concat(item);
        memo[index] = flatten;    return flatten;
    });// badinbox.filter((msg) => {    const {subject, author} = msg;    if (subject === 'Mockingbird') {        return author === 'Harper Lee';
        } else {        return false;
        }
    });// goodinbox.filter((msg) => {    const {subject, author} = msg;    if (subject === 'Mockingbird') {        return author === 'Harper Lee';
        }    return false;
    });
  • 3.7 如果数组有多行,请在打开和关闭数组括号前使用换行符。eslint: array-bracket-newline

    // badconst arr = [
        [0, 1], [2, 3], [4, 5],
    ];const objectInArray = [{    id: 1,
    }, {    id: 2,
    }];const numberInArray = [    1, 2,
    ];// goodconst arr = [[0, 1], [2, 3], [4, 5]];const objectInArray = [
        {        id: 1,
        },
        {        id: 2,
        },
    ];const numberInArray = [    1,    2,
    ];
  • 3.8 禁止使用稀疏数组。eslint: no-sparse-arrays

    // badconst color = ['red',, 'blue'];// goodconst color = ['red', 'blue'];

返回目录

<a name="strings"></a>

字符串 Strings

  • 4.1 <a name='4.1'></a> 字符串使用单引号 '' 。eslint: quotes

    // badconst name = "Capt. Janeway";// bad - template literals should contain interpolation or newlinesconst name = `Capt. Janeway`;// goodconst name = 'Capt. Janeway';
  • 4.2 <a name='4.2'></a> 字符串超过 ==80== 个字节应该使用字符串连接号换行。

  • 4.3 <a name='4.3'></a> 注:过度使用字串连接符号可能会对性能造成影响。jsPerf讨论。但是打包工具会在打包压缩后的代码中处理好字符串的拼接。而且字串连接符号对性能影响有限。

    // badconst errorMessage = 'This is a super long error that was thrown because of Batman. When you stop to think about how Batman had anything to do with this, you would get nowhere fast.';// badconst errorMessage = 'This is a super long error that was thrown because \
    of Batman. When you stop to think about how Batman had anything to do \
    with this, you would get nowhere \
    fast.';// goodconst errorMessage = 'This is a super long error that was thrown because ' +  'of Batman. When you stop to think about how Batman had anything to do ' +  'with this, you would get nowhere fast.';

<a name="es6-template-literals"></a>

  • 4.4 <a name='4.4'></a> 程序化生成字符串时,使用模板字符串代替字符串连接。eslint: prefer-template template-curly-spacing

    为什么?模板字符串更为简洁,更具可读性。

    // badconst sayHi = function (name) {  return 'How are you, ' + name + '?';
    };// badconst sayHi = function (name) {  return ['How are you, ', name, '?'].join();
    };// badconst sayHi = function (name) {  return `How are you, ${ name }?`;
    };// goodconst sayHi = function (name) {  return `How are you, ${name}?`;
    };

<a name="strings--eval"></a>

  • 4.5 <a name='4.5'></a>不要在字符串中使用 eval(),这个方法有要多缺陷。eslint: no-eval

<a name="strings--escaping"></a>

  • 4.6 <a name='4.6'></a> 不要在字符串中使用不必要的转义字符。 eslint: no-useless-escape

    为什么?反斜杠导致代码不可读,因此应该在必要的时候使用。

    // badconst foo = '\'this\' \i\s \"quoted\"';// goodconst foo = '\'this\' is "quoted"';const foo = `my name is '${name}'`;

返回目录

<a name="functions"></a>

函数 Functions

  • 5.1 <a name='5.1'></a> 使用函数表达式代替函数声明(顶级函数不受限制)。eslint: func-style

    为什么?函数声明会把整个函数提升(hoisted),这导致在函数定义之前就可以引用该函数。这会影响代码的可读性和可维护性。

    // badfunction foo() {    // ...}// goodconst foo = function () {    // ...};
  • 5.2 <a name='5.2'></a> 用括号包裹立即执行函数表达式。 eslint: wrap-iife

    为什么?立即执行函数表达式是个单独的模块,用括号将函数和表示函数执行的括号包裹起来,会使代码更清晰。

    // 立即执行函数表达式 (IIFE)(function () {  console.log('Welcome to the Internet. Please follow me.');
    }());
  • 5.3 <a name='5.3'></a> 永远不要在一个非函数代码块(ifwhile 等)中声明一个函数,应该把函数赋给代码块外部的一个变量。浏览器允许你这么做,但它们的解析表现不一致。eslint: no-loop-func
    注意: ECMA-262 把 block 定义为一组语句。函数声明不是语句。阅读 ECMA-262 关于这个问题的说明

  • //badif (foo) {    function test () {
            ...
        }//goodlet test;if(foo) {
        test = () => {
            ...
        }
    }
  • 5.4 <a name='6.4'></a> 永远不要把参数命名为 arguments。这将取代原来函数作用域内的 arguments 对象。

    // badconst nope = function (name, options, arguments) {    // ...stuff...};// goodconst yup = function (name, options, args) {    // ...stuff...};

<a name="es6-rest"></a>

  • 5.5 <a name='5.5'></a> 不要使用 arguments。可以选择 rest 语法 ... 替代。eslint: prefer-rest-params

    为什么?使用 ... 能明确你要传入的参数。另外 rest 参数是一个真正的数组,而 arguments 是一个类数组。

    // badconst concatenateAll = function () {    const args = Array.prototype.slice.call(arguments);    return args.join('');
    };// goodconst concatenateAll = function (...args) {    return args.join('');
    };

<a name="es6-default-parameters"></a>

  • 5.6 <a name='5.6'></a> 直接给函数的参数指定默认值,不要使用一个变化的函数参数。

    // really badconst handleThings = function (opts) {    // 不!我们不应该改变函数参数。
        // 更加糟糕: 如果参数 opts 是 false 的话,它就会被设定为一个对象。
        // 但这样的写法会造成一些 Bugs。
        //(译注:例如当 opts 被赋值为空字符串,opts 仍然会被下一行代码设定为一个空对象。)
        opts = opts || {};    // ...};// still badconst handleThings = function (opts) {    if (opts === void 0) {
            opts = {};
        }    // ...};// goodconst handleThings = function (opts = {}) {    // ...};
  • 5.7 <a name='5.7'></a> 直接给函数参数赋值时需要避免副作用。

    为什么?因为这样的写法让人感到很困惑。

    var b = 1;// badconst count = function (a = b++) {    console.log(a);
    };
    count();  // 1count();  // 2count(3); // 3count();  // 3

<a name="functions--constructor"></a><a name="5.8"></a>

  • 5.8 不要使用构造函数的方式创建一个新函数。 eslint: no-new-func

    为什么?用构造函数的方式创建函数类似于用 eval() 创建字符串,会有很多缺陷。

    // badvar add = new Function('a', 'b', 'return a + b');// still badvar subtract = Function('a', 'b', 'return a - b');

(所以是不使用class语法的意思吗)
<a name="functions--signature-spacing"></a><a name="5.9"></a>

  • 6.9 function 关键词的空格。 eslint: space-before-function-paren space-before-blocks

    为什么?保持一致性,这样在添加和删除函数时就不需要修改空格了。

    // badconst fetch = function(){};const get = function (){};const hold = function() {};// goodconst fetch = function () {};

<a name="functions--spread-vs-apply"></a><a name="5.10"></a>

  • 5.10 优先使用扩展运算符 ... 来调用参数可变的函数。 eslint: prefer-spread

    为什么? 这样写代码更干净,不必提供上下文。

    // badconst x = [1, 2, 3, 4, 5];console.log.apply(console, x);// goodconst x = [1, 2, 3, 4, 5];console.log(...x);// badnew (Function.prototype.bind.apply(Date, [null, 2016, 8, 5]));// goodnew Date(...[2016, 8, 5]);

<a name="functions--defaults-last"></a><a name="5.11"></a>

  • 5.11 有默认值的参数总是放在最后一个。

    // badconst handleThings = function (opts = {}, name) {    // ...};// goodconst handleThings = function (name, opts = {}) {    // ...};```  ```

<a name="functions--complexity"></a><a name="5.12"></a>

  • 5.12 建议函数的圈复杂度(代码的独立现行路径条数)==在20以内==。 eslint: complexity

为什么? 这样可以减少函数的复杂度,使函数容易看懂。

返回目录

<a name="arrow-functions"></a>

箭头函数

  • 6.1 <a name='6.1'></a> 当你必须使用函数表达式(或传递一个匿名函数)时,使用箭头函数符号。eslint: prefer-arrow-callback, arrow-spacing

    为什么?因为箭头函数创造了新的一个 this 执行环境(译注:参考 Arrow functions - JavaScript | MDNES6 arrow functions, syntax and lexical scoping),通常情况下都能满足你的需求,而且这样的写法更为简洁。

    为什么不?如果你有一个相当复杂的函数,你或许可以把逻辑部分转移到一个函数声明上。

    // bad[1, 2, 3].map(function (num) {    const sum = num + 1;    return num * sum;
    });// good[1, 2, 3].map((num) => {    const sum = num + 1;    return num * sum;
    });
  • 6.2 <a name='6.2'></a> 如果一个函数适合用一行写出并且只有一个参数,那就把花括号、圆括号和 return 都省略掉。如果不是,那就不要省略。eslint: arrow-parens, arrow-body-style

    为什么?语法糖。在链式调用中可读性很高。

    // bad[1, 2, 3].map((number) => {    const nextNumber = number + 1;    `A string containing the ${nextNumber}.`;
    });// good[1, 2, 3].map((number) => `A string containing the ${number}.`);// good[1, 2, 3].map((number) => {    const nextNumber = number + 1;    return `A string containing the ${nextNumber}.`;
    });// good[1, 2, 3].map((number, index) => ({
        [index]: number,
    }));// No implicit return with side effectsconst foo = function (callback) {    const val = callback();    if (val === true) {        // Do something if callback returns true
        }
    };let bool = false;// badfoo(() => bool = true);// goodfoo(() => {
        bool = true;
    });

<a name="arrows--paren-wrap"></a><a name="6.3"></a>

  • 6.3 如果一个表达式跨行了,必须用括号括起来增强可读性。

    为什么?这使得函数开始和结束的位置非常清晰。

    // bad['get', 'post', 'put'].map((httpMethod) => Object.prototype.hasOwnProperty.call(
            httpMagicObjectWithAVeryLongName,
            httpMethod,
        )
    );// good['get', 'post', 'put'].map((httpMethod) => (    Object.prototype.hasOwnProperty.call(
            httpMagicObjectWithAVeryLongName,
            httpMethod,
        )
    ));

<a name="arrows--one-arg-parens"></a><a name="6.4"></a>

  • 6.4 入参必须用括号括起来。eslint: arrow-parens

    为什么? 保持代码清晰和一致性。

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

<a name="arrows--confusing"></a><a name="6.5"></a>

  • 6.5 避免混用箭头函数的语法 (=>) 和比较运算符 (<=, >=)。 eslint: no-confusing-arrow

    // badconst itemHeight = item => item.height > 1000 ? item.largeSize : item.smallSize;// badconst itemHeight = (item) => item.height > 1000 ? item.largeSize : item.smallSize;// goodconst itemHeight = (item) => (item.height > 1000 ? item.largeSize : item.smallSize);// goodconst itemHeight = (item) => {    const {
            height,
            largeSize,
            smallSize,
        } = item;    return height > 1000 ? largeSize : smallSize;
    };

返回目录

<a name="modules"></a>

模块 Modules

  • 7.1 <a name='7.1'></a> 总是使用模组 (import/export) 而不是其他非标准模块系统。你可以编译为你喜欢的模块系统。

    为什么?模块化是未来趋势。

    // badconst AirbnbStyleGuide = require('./AirbnbStyleGuide');module.exports = AirbnbStyleGuide.es6;// okimport AirbnbStyleGuide from './AirbnbStyleGuide';export default AirbnbStyleGuide.es6;// bestimport {es6} from './AirbnbStyleGuide';export default es6;
  • 7.2 <a name='7.2'></a> 不要使用通配符 import。

    为什么?这样能确保你只有一个默认 export。

    // badimport * as AirbnbStyleGuide from './AirbnbStyleGuide';// goodimport AirbnbStyleGuide from './AirbnbStyleGuide';
  • 7.3 <a name='7.3'></a>不要从 import 中直接 export。

    为什么?虽然一行代码简洁明了,但让 import 和 export 各司其职让事情能保持一致。

    // bad// filename es6.jsexport {es6 as default} from './airbnbStyleGuide';// good// filename es6.jsimport {es6} from './AirbnbStyleGuide';export default es6;

<a name="modules--no-duplicate-imports"></a>

  • 7.4 不要多次 import 同一个文件。eslint: no-duplicate-imports

    Why? 多个地方引入同一个文件会使代码难以维护。

    // badimport foo from 'foo';// … some other imports … //import {named1, named2} from 'foo';// goodimport foo, {named1, named2} from 'foo';// goodimport foo, {
        named1,
        named2,
    } from 'foo';

返回目录

<a name="iterators-and-generators"></a>

Iterators and Generators

  • 8.1 <a name='8.1'></a> 不要使用 iterators。使用高阶函数例如 map()reduce() 替代 for-offor-in。eslint: no-iterator no-restricted-syntax

    为什么?这加强了我们不变的规则。处理纯函数的回调值更易读,这比它带来的副作用更重要。

    使用 map()every()filter()find()findIndex()reduce()some()...来遍历数组,使用 Object.keys()Object.values()Object.entries() 生成数组以便遍历对象。

    const numbers = [1, 2, 3, 4];// badlet sum = 0;for (let num of numbers) {
        sum += num;
    }
    
    sum === 10;// goodlet sum = 0;
    numbers.forEach((num) => {
        sum += num;
    });
    sum === 10;// best (use the functional force)const sum = numbers.reduce((total, num) => total + num, 0);
    sum === 10;
  • 8.2 <a name='8.2'></a> 现在还不要使用 generators。

    为什么?因为它们现在还没法很好地编译到 ES5。

返回目录

<a name="properties"></a>

属性 Properties

  • 9.1 <a name='9.1'></a> 使用 . 来访问对象的属性。eslint: dot-notation

    const luke = {    jedi: true,    age: 12,
    };// badconst isJedi = luke['jedi'];// goodconst isJedi = luke.jedi;
  • 9.2 <a name='9.2'></a> 当通过变量访问属性时使用中括号 []

    const luke = {    jedi: true,    age: 12,
    };const getProp = (prop) => {    return luke[prop];
    }const isJedi = getProp('jedi');

返回目录

<a name="variables"></a>

变量 Variables

<a name="variables--no-chain-assignment"></a><a name="10.1"></a>

  • 11.1 不要链式的给变量赋值。eslint: no-multi-assign

    为什么?链式变量赋值会创建隐式的全局变量。

    // bad(function example() {    // JavaScript interprets this as
        // let a = ( b = ( c = 1 ) );
        // The let keyword only applies to variable a; variables b and c become
        // global variables.
        let cat = dog = bird = 1;
    }());console.log(cat); // throws ReferenceErrorconsole.log(dog); // 1console.log(bird); // 1// good(function example() {    let cat = 1;    let dog = cat;    let bird = cat;
    }());console.log(cat); // throws ReferenceErrorconsole.log(dogb); // throws ReferenceErrorconsole.log(bird); // throws ReferenceError// the same applies for `const`

<a name="variables--unary-increment-decrement"></a><a name="10.2"></a>

  • 10.2 避免使用累加和累减符号 (++, --)。 eslint no-plusplus

    为什么?根据 eslint 文档,累加和累减会受到自动插入分号的影响,并可能导致一些意外情况发生。

    // badconst array = [1, 2, 3];let num = 1;
    num++;
    --num;let sum = 0;let truthyCount = 0;for (let i = 0; i < array.length; i++) {    let value = array[i];
        sum += value;    if (value) {
            truthyCount++;
        }
    }// goodconst array = [1, 2, 3];let num = 1;
    num += 1;
    num -= 1;const sum = array.reduce((a, b) => a + b, 0);const truthyCount = array.filter(Boolean).length;

<a name="variables--no-magic-numbers"></a><a name="10.3"></a>

  • 10.3 避免使用魔法数字(白名单如下)。 eslint no-magic-numbers

    为什么?魔法数字让人难难以明白开发者的意图是什么,破坏代码的可读性和可维护性。

    // 以下为魔法数字白名单let magicNumberIgnore = [];// baisc numbermagicNumberIgnore = magicNumberIgnore.concat([    -1, 0, 100, 1000, 10000]);// datetime numbermagicNumberIgnore = magicNumberIgnore.concat([    12, 24, 60, 3600]);// httpcode numbermagicNumberIgnore = magicNumberIgnore.concat([    200,    301, 302, 303, 304,    400, 401, 402, 403, 404,    500, 501, 502, 503, 504]);// bit numbermagicNumberIgnore = magicNumberIgnore.concat([    1024]);// number 1-49for (i = 1; i <= 49; i++) {    if (magicNumberIgnore.indexOf(i) === -1) {
    
            magicNumberIgnore.push(i);
    
        }
    
    }
    // badlet soldNum = 102;let initNum = 80;// goodconst APPLE = 102;const STOCK = 80;let soldNum = APPLE;let initNum = STOCK;

返回目录

<a name="hoisting"></a>

Hoisting

  • 11.1 <a name='11.1'></a> var 声明会被提升至该作用域的顶部,但它们赋值不会提升。letconst 被赋予了一种称为「暂时性死区(Temporal Dead Zones, TDZ)」的概念。这对于了解为什么 typeof 不再安全相当重要。

    // 我们知道这样运行不了// (假设 notDefined 不是全局变量)const example = function () {    console.log(notDefined); // => throws a ReferenceError};// 由于变量提升的原因,// 在引用变量后再声明变量是可以运行的。// 注:变量的赋值 `true` 不会被提升。const example = function () {    console.log(declaredButNotAssigned); // => undefined
        var declaredButNotAssigned = true;
    };// 编译器会把函数声明提升到作用域的顶层,// 这意味着我们的例子可以改写成这样:const example = function () {    let declaredButNotAssigned;    console.log(declaredButNotAssigned); // => undefined
        declaredButNotAssigned = true;
    };// 使用 const 和 letconst example = function () {    console.log(declaredButNotAssigned); // => throws a ReferenceError
        console.log(typeof declaredButNotAssigned); // => throws a ReferenceError
        const declaredButNotAssigned = true;
    };
  • 11.2 <a name='11.2'></a> 匿名函数表达式的变量名会被提升,但函数内容并不会。

    const example = function () {    console.log(anonymous); // => undefined
    
        anonymous(); // => TypeError anonymous is not a function
    
        var anonymous = function() {        console.log('anonymous function expression');
        };
    };
  • 11.3 <a name='11.3'></a> 命名的函数表达式的变量名会被提升,但函数名和函数内容并不会。

    const example = function () {    console.log(named); // => undefined
    
        named(); // => TypeError named is not a function
    
        superPower(); // => ReferenceError superPower is not defined
    
        var named = function superPower() {        console.log('Flying');
        };
    };// the same is true when the function name// is the same as the variable name.const example = function () {    console.log(named); // => undefined
    
        named(); // => TypeError named is not a function
    
        var named = function named() {        console.log('named');
        }
    };
  • 11.4 <a name='11.4'></a> 函数声明的名称和函数体都会被提升。

    const example = function () {
        superPower(); // => Flying
    
        function superPower() {        console.log('Flying');
        }
    };
  • 想了解更多信息,参考 Ben CherryJavaScript Scoping & Hoisting

返回目录

<a name="comparison-operators--equality"></a>

比较运算符和等号

  • 12.1 <a name='12.1'></a> 使用 ===!== 而不是 ==!=。eslint: eqeqeq

  • 12.2 <a name='12.2'></a> 条件表达式例如 if 语句通过抽象方法 ToBoolean 强制计算它们的表达式并且总是遵守下面的规则:

    if ([0] && []) {    // true
        // an array (even an empty one) is an object, objects will evaluate to true}
    • 对象 被计算为 true

    • Undefined 被计算为 false

    • Null 被计算为 false

    • 布尔值 被计算为 布尔的值

    • 数字 如果是 +0、-0、或 NaN 被计算为 false, 否则为 true

    • 字符串 如果是空字符串 '' 被计算为 false,否则为 true

  • 12.3 <a name='12.3'></a> 布尔值使用简写,但是需要显示比较字符串和数字。

    // badif (isValid === true) {    // ...}// goodif (isValid) {    // ...}// badif (name) {    // ...}// goodif (name !== '') {    // ...}// badif (collection.length) {    // ...}// goodif (collection.length > 0) {    // ...}
  • 12.4 <a name='12.4'></a> 想了解更多信息,参考 Angus Croll 的 Truth Equality and JavaScript

<a name="comparison--switch-blocks"></a><a name="12.5"></a>

  • 12.5casedefault 包含的子句中有词法声明时,用大括号包裹。(例如 let, const, function, and class). eslint: no-case-declarations

    为什么?词法声明在整个 switch 块中是可见的,但只有在代码执行到 case 中变量被赋值时才会被初始化,当多个 case 子句中定义相同的变量是时会出现问题。

    // badswitch (foo) {    case 1:        let xx = 1;        break;    case 2:        const yy = 2;        break;    case 3:        const func = function () {        // ...
            };        break;    default:
            get();
    }// goodswitch (foo) {    case 1: {        let xx = 1;        break;
        }    case 2: {        const yy = 2;        break;
        }    case 3: {        const func = function () {        // ...
            };        break;
        }    case 4:
            bar();        break;    default: {
            get();
        }
    }

<a name="comparison--nested-ternaries"></a><a name="12.6"></a>

  • 12.6 三元表达式不能嵌套使用。 eslint: no-nested-ternary

    // badconst foo = maybe1 > maybe2
        ? "bar"
        : value1 > value2 ? "baz" : null;// split into 2 separated ternary expressionsconst maybeNull = value1 > value2 ? 'baz' : null;// betterconst foo = maybe1 > maybe2
        ? 'bar'
        : maybeNull;// bestconst foo = maybe1 > maybe2 ? 'bar' : maybeNull;

<a name="comparison--unneeded-ternary"></a><a name="12.7"></a>

  • 12.7 避免出现不必要的三元表达式。 eslint: no-unneeded-ternary

    // badconst foo = maybe1 ? maybe1 : maybe2;const bar = correct ? true : false;const baz = wrong ? false : true;// goodconst foo = maybe1 || maybe2;const bar = !!correct;const baz = !wrong;

<a name="comparison--no-mixed-operators"></a>

  • 12.8 当混用操作符时,要用括号括起来。唯一的例外是标准的算术运算符 (+, -, *, & /),因为它们很容易理解。 eslint: no-mixed-operators

    为什么?这提高了代码的可读性,并且使得开发者的意图很清晰。

    // badconst foo = p1 && p2 < 0 || p3 > 0 || p4 + 1 === 0;// badconst bar = p1 ** p2 - 5 % p4;// bad// one may be confused into thinking (p1 || p2) && p3if (p1 || p2 && p3) {    return p4;
    }// goodconst foo = (p1 && p2 < 0) || p3 > 0 || (p4 + 1 === 0);// goodconst bar = (p1 ** p2) - (5 % p4);// goodif (p1 || (p2 && p3)) {    return p4;
    }// goodconst bar = p1 + (p2 / p3 * p4);

返回目录

<a name="blocks"></a>

代码块 Blocks

  • 13.1 <a name='13.1'></a> 使用大括号包裹所有的多行代码块。eslint: nonblock-statement-body-position

    // badif (test)    return false;// goodif (test) {    return false;
    }// goodif (test) {    return false;
    }// badconst func = function () {return false};// goodconst func = function () {    return false;
    };
  • 13.2 <a name='13.2'></a> 如果通过 ifelse 使用多行代码块,把 else 放在 if 代码块关闭括号的同一行。eslint: brace-style

    // badif (test) {
        thing1();
        thing2();
    }else {
        thing3();
    }// goodif (test) {
        thing1();
        thing2();
    } else {
        thing3();
    }

<a name="blocks--no-else-return"></a><a name="13.3"></a>

  • 13.3 如果 if 代码块中肯定会执行 return 语句,那么后面的 else 就没必要写了。如果在 else if 代码块中又有 if,并且 if 代码块会执行return 语句,那么可以分拆成多个 if 代码块。 eslint: no-else-return

    // badconst foo = function () {    if (correct) {        return correct;
        } else {        return wrong;
        }
    };// badconst cats = function () {    if (correct) {        return correct;
        } else if (wrong) {        return wrong;
        }
    };// badconst dogs = function () {    if (correct) {        return correct;
        } else {        if (wrong) {            return wrong;
            }
        }
    };// goodconst foo = function () {    if (correct) {        return correct;
        }    return wrong;
    };// goodconst cats = function () {    if (correct) {        return correct;
        }    if (wrong) {        return wrong;
        }    return false;
    };//goodconst dogs = function (correct) {    if (correct) {        if (confuse) {            return wrong;
            }
        } else {        return confuse;
        }
    };

返回目录

<a name="comments"></a>

注释 Comments

  • 14.1 <a name='14.1'></a> 所有的注释开头都加一个空格,这样更利于阅读。eslint: spaced-comment

    // bad//is current tabconst active = true;// good// is current tabconst active = true;// bad/**
     *make() returns a new element
     *based on the passed-in tag name
     */const make = function (tag) {    // ...
    
        return element;
    };// good/**
     * make() returns a new element
     * based on the passed-in tag name
     */const make = function (tag) {    // ...
    
        return element;
    };
  • 14.2 <a name='14.2'></a> 给注释增加 FIXMETODO 的前缀可以帮助其他开发者快速了解这是一个需要复查的问题,或是给需要实现的功能提供一个解决方式。这将有别于常见的注释,因为它们是可操作的。使用 FIXME -- need to figure this out 或者 TODO -- need to implement

  • 14.3 <a name='14.3'></a> 使用 // FIXME: 标注问题。

    class Calculator {    constructor() {        // FIXME: shouldn't use a global here
            total = 0;
        }
    }
  • 14.4 <a name='14.4'></a> 使用 // TODO: 标注问题的解决方式。

    class Calculator {    constructor() {        
            // TODO: total should be configurable by an options param
            this.total = 0;
        }
    }

返回目录

<a name="whitespace"></a>

空格 Whitespace

  • 15.1 <a name='15.1'></a> 使用 4 个空格作为缩进。eslint: indent

    // badconst func = function () {
    ∙∙const name = '';
    }// badconst func = function () {
    ∙const name = '';
    }// goodconst func = function () {
    ∙∙∙∙const name = '';
    }
  • 15.2 <a name='15.2'></a> 在大括号前放一个空格。eslint: space-before-blocks

    // badconst test = function (){    console.log('test');
    }// goodconst test = function () {    console.log('test');
    }// baddog.set('attr',{    age: '1 year',    breed: 'Bernese Mountain Dog',
    });// gooddog.set('attr', {    age: '1 year',    breed: 'Bernese Mountain Dog',
    });
  • 15.3 <a name='15.3'></a> 在控制语句(ifwhile 等)的小括号前放一个空格。在函数调用及声明中,不要在函数的参数列表前加空格。eslint: keyword-spacing

    // badif(isJedi) {
        fight ();
    }// goodif (isJedi) {
        fight();
    }// badconst fight = function() {    console.log ('Swooosh!');
    }// goodconst fight = function () {    console.log('Swooosh!');
    }
  • 15.4 <a name='15.4'></a> 使用空格把运算符隔开。eslint: space-infix-ops

    // badconst i=j+5;// goodconst i = j + 5;
  • 15.5 <a name='15.5'></a> 在文件末尾插入一个空行。eslint: eol-last

    // badimport {es6} from './AirbnbStyleGuide';    // ...export default es6;
    // badimport {es6} from './AirbnbStyleGuide';    // ...export default es6;
    
    // goodimport {es6} from './AirbnbStyleGuide';    // ...export default es6;
  • 15.6 <a name='15.6'></a> 在使用长方法链时进行缩进。使用前面的点 . 强调这是方法调用而不是新语句。eslint: newline-per-chained-call no-whitespace-before-property

    // bad$('#items').find('.selected').highlight().end().find('.open').updateCount();// bad$('#items').
        find('.selected').
            highlight().
            end().
        find('.open').
            updateCount();// good$('#items')
        .find('.selected')
        .highlight()
        .end()
        .find('.open')
        .updateCount();// badconst leds = stage.selectAll('.led').data(data).enter().append('svg:svg').class('led', true)
        .attr('width', (radius + margin) * 2).append('svg:g')
        .attr('transform', 'translate(' + (radius + margin) + ',' + (radius + margin) + ')')
        .call(tron.led);// goodconst leds = stage.selectAll('.led')
        .data(data)
        .enter()
        .append('svg:svg')
        .classed('led', true)
        .attr('width', (radius + margin) * 2)
        .append('svg:g')
        .attr('transform', `translate(${  radius + margin  },${  radius + margin  })`)
        .call(tron.led);// goodconst leds = stage.selectAll('.led').data(data);
  • 15.7 <a name='15.7'></a> 在块末和新语句前插入空行。

    // badif (foo) {    return bar;
    }return baz;// goodif (foo) {    return bar;
    }return baz;// badconst obj = {
        foo() {
        },
        bar() {
        },
    };return obj;// goodconst obj = {
        foo() {
        },
    
        bar() {
        },
    };return obj;

<a name="whitespace--padded-blocks"></a><a name="15.8"></a>

  • 15.8 不要在 block 代码块中加空行。 eslint: padded-blocks

    // badconst bar = function () {    console.log(foo);
    
    };// badif (baz) {    console.log(qux);
    } else {    console.log(foo);
    
    }// badclass Foo {    constructor(bar) {        this.bar = bar;
        }
    }// goodconst bar = function () {    console.log(foo);
    };// goodif (baz) {    console.log(qux);
    } else {    console.log(foo);
    }

<a name="whitespace--in-parens"></a><a name="15.9"></a>

  • 15.9 不要在小括号中加空格。 eslint: space-in-parens

    // badconst bar = function ( foo ) {    return foo;
    };// goodconst bar = function (foo) {    return foo;
    };// badif ( foo ) {    console.log(foo);
    }// goodif (foo) {    console.log(foo);
    }

<a name="whitespace--in-brackets"></a><a name="15.10"></a>

  • 15.10 不要在中括号中加空格。 eslint: array-bracket-spacing

    // badconst foo = [ 1, 2, 3 ];console.log(foo[ 0 ]);// goodconst foo = [1, 2, 3];console.log(foo[0]);

<a name="whitespace--in-braces"></a><a name="15.11"></a>

  • 15.11 不要在大括号中加空格。 eslint: object-curly-spacing

    // badconst foo = { clark: 'kent' };// goodconst foo = {clark: 'kent'};

<a name="whitespace--max-len"></a><a name="15.12"></a>

  • 15.12 尽量控制一行代码在==100==个字符以内(包括空格)。字符串、字符串模板和 URL 不受限制。eslint: max-len

    为什么?这么做保证了可读性和可维护性。

    // badconst foo = jsonData && jsonData.foo && jsonData.foo.bar && jsonData.foo.bar.baz && jsonData.foo.bar.baz.quux && jsonData.foo.bar.baz.quux.xyzzy;// bad$.ajax({ method: 'POST', url: 'https://airbnb.com/', data: { name: 'John' } }).done(() => console.log('Congratulations!')).fail(() => console.log('You have failed this city.'));// goodconst foo = jsonData
        && jsonData.foo
        && jsonData.foo.bar
        && jsonData.foo.bar.baz
        && jsonData.foo.bar.baz.quux
        && jsonData.foo.bar.baz.quux.xyzzy;// good$.ajax({    method: 'POST',    url: 'https://airbnb.com/',    data: {        name: 'John',
        },
    })
        .done(() => {        // do something...
        })
        .fail(() => {        // do something...
        });

返回目录

<a name="commas"></a>

逗号 Commas

  • 16.1 <a name='16.1'></a> 行首逗号:禁用。eslint: comma-style

    // badconst story = [
        once
        , upon
        , aTime
    ];// goodconst story = [
        once,
        upon,
        aTime,
    ];// badconst hero = {    firstName: 'Ada'
        , lastName: 'Lovelace'
        , birthYear: '1815'
        , superPower: 'computers'};// goodconst hero = {    firstName: 'Ada',    lastName: 'Lovelace',    birthYear: '1815',    superPower: 'computers',
    };
  • 16.2 <a name='16.2'></a> 增加结尾的逗号:需要。eslint: comma-dangle

    为什么? 这会让 git diffs 更干净。另外,像 babel 这样的转译器会移除结尾多余的逗号,也就是说你不必担心老旧浏览器的尾逗号问题

    // bad - git diff without trailing commaconst hero = {     firstName: 'Florence',
    -    lastName: 'Nightingale'+    lastName: 'Nightingale',
    +    inventorOf: ['coxcomb graph', 'modern nursing']
    };// good - git diff with trailing commaconst hero = {     firstName: 'Florence',     lastName: 'Nightingale',
    +    inventorOf: ['coxcomb chart', 'modern nursing'],
    };// badconst hero = {    firstName: 'Dana',    lastName: 'Scully'};const heroes = [    'Batman',    'Superman'];// goodconst hero = {    firstName: 'Dana',    lastName: 'Scully',
    };const heroes = [    'Batman',    'Superman',
    ];// badconst createHero = function (
        firstName,
        lastName,
        inventorOf) {    // does nothing};// goodconst createHero = function (
        firstName,
        lastName,
        inventorOf,) {    // does nothing};// good (note that a comma must not appear after a "rest" element)const createHero = function (
        firstName,
        lastName,
        inventorOf,
        ...heroArgs) {    // does nothing};// badcreateHero(
        firstName,
        lastName,
        inventorOf
    );// goodcreateHero(
        firstName,
        lastName,
        inventorOf,
    );// goodcreateHero(
        firstName,
        lastName,
        inventorOf,
        ...heroArgs
    );

返回目录

<a name="semicolons"></a>

分号 Semicolons

  • 17.1 <a name='17.1'></a> 使用分号。eslint: semi

    Why? When JavaScript encounters a line break without a semicolon, it uses a set of rules called Automatic Semicolon Insertion to determine whether or not it should regard that line break as the end of a statement, and (as the name implies) place a semicolon into your code before the line break if it thinks so. ASI contains a few eccentric behaviors, though, and your code will break if JavaScript misinterprets your line break. These rules will become more complicated as new features become a part of JavaScript. Explicitly terminating your statements and configuring your linter to catch missing semicolons will help prevent you from encountering issues.

    // bad - raises exceptionconst luke = {}const leia = {}
    [luke, leia].forEach(jedi => jedi.father = 'vader')// bad - raises exceptionconst reaction = "No! That's impossible!"(async function meanwhileOnTheFalcon(){    // handle `leia`, `lando`, `chewie`, `r2`, `c3p0`
        // ...}())// bad - returns `undefined` instead of the value on the next line - always happens when `return` is on a line by itself because of ASI!const foo = foo() {    return
            'search your feelings, you know it to be foo'};// goodconst luke = {};const leia = {};
    [luke, leia].forEach((jedi) => {
        jedi.father = 'vader';
    });// goodconst reaction = "No! That's impossible!";
    (async function meanwhileOnTheFalcon(){    // handle `leia`, `lando`, `chewie`, `r2`, `c3p0`
        // ...}());// goodconst foo = foo() {    return 'search your feelings, you know it to be foo';
    };

    Read more.

返回目录

<a name="type-casting--coercion"></a>

类型转换

  • 18.1 <a name='18.1'></a> 在语句开始时执行类型转换。

  • 18.2 <a name='18.2'></a> 字符串:eslint: no-new-wrappers

    // => this.reviewScore = 9;// badconst totalScore = new String(this.reviewScore); // typeof totalScore is "object" not "string"// badconst totalScore = this.reviewScore + ''; // invokes this.reviewScore.valueOf()// badconst totalScore = this.reviewScore.toString(); // isn’t guaranteed to return a string// goodconst totalScore = String(this.reviewScore);
  • 18.3 <a name='18.3'></a> 对数字使用 parseInt 转换,并带上类型转换的基数。(不强制)eslint: radix no-new-wrappers

    const inputValue = '4';// badconst val = new Number(inputValue);// badconst val = +inputValue;// badconst val = inputValue >> 0;// badconst val = parseInt(inputValue);// goodconst val = Number(inputValue);// goodconst val = parseInt(inputValue, 10);
  • 18.4 <a name='18.4'></a> 如果因为某些原因 parseInt 成为你所做的事的瓶颈而需要使用位操作解决性能问题时,留个注释说清楚原因和你的目的。

    // good/**
     * 使用 parseInt 导致我的程序变慢,
     * 改成使用位操作转换数字快多了。
     */const val = inputValue >> 0;
  • 18.5 <a name='18.5'></a> 注: 小心使用位操作运算符。数字会被当成 64 位值,但是位操作运算符总是返回 32 位的整数(参考)。位操作处理大于 32 位的整数值时还会导致意料之外的行为。关于这个问题的讨论。最大的 32 位整数是 2,147,483,647:

    2147483647 >> 0 //=> 21474836472147483648 >> 0 //=> -21474836482147483649 >> 0 //=> -2147483647
  • 18.6 <a name='18.6'></a> 布尔:eslint: no-new-wrappers

    const age = 0;// badconst hasAge = new Boolean(age);// goodconst hasAge = Boolean(age);// goodconst hasAge = !!age;

返回目录

<a name="naming-conventions"></a>

命名规则

  • 19.1 <a name='19.1'></a> 避免单字母命名(e、i、j、v、k、t除外)。避免超长变量名(长度不超过60)。命名应具备描述性和可读性。eslint: id-length

    // badconst q = function () {    // ...stuff...};// badconst getWebBeibei11MaylikeRecommendPageSize20Page1XidMTcxNTM2Njcxsa = function () {    // ...stuff...};// goodconst query = function () {    // ...stuff..。};
  • 19.2 <a name='19.2'></a> 使用驼峰式命名对象、函数和实例。(对象的属性不限制)eslint: camelcase

    // badconst OBJEcttsssss = {};const this_is_my_object = {};// goodconst thisIsMyObject = {};const thisIsMyFunction = function () {}
  • 19.3 <a name='19.3'></a> 使用帕斯卡式命名构造函数或类。eslint: new-cap

    // badfunction user(options) {  this.name = options.name;
    }const bad = new user({  name: 'nope',
    });// goodclass User {  constructor(options) {    this.name = options.name;
      }
    }const good = new User({  name: 'yup',
    });
  • 19.4 <a name='19.4'></a> 别保存 this 的引用。使用箭头函数或 Function#bind。

    // badconst foo = function () {    const self = this;    return function() {        console.log(self);
        };
    };// badconst foo = function () {    const that = this;    return function() {        console.log(that);
        };
    };// goodconst foo = function () {    return () => {        console.log(this);
        };
    };
  • 19.5 <a name='19.5'></a> 如果你的文件只输出一个类,那你的文件名必须和类名完全保持一致。

    // file contentsclass CheckBox {    // ...}export default CheckBox;// in some other file// badimport CheckBox from './checkBox';// badimport CheckBox from './check_box';// goodimport CheckBox from './CheckBox';
  • 19.6 <a name='19.6'></a> 当你导出默认的函数时使用驼峰式命名。你的文件名必须和函数名完全保持一致。

    const makeStyleGuide = function () {
    }export default makeStyleGuide;
  • 19.7 <a name='19.7'></a> 当你导出单例、函数库、空对象时使用帕斯卡式命名。

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

返回目录



作者:小红依
链接:https://www.jianshu.com/p/3eee4acbf071


打开App,阅读手记
0人推荐
发表评论
随时随地看视频慕课网APP