Skip to content

Javascript 风格指南

simplexcspp edited this page Sep 21, 2019 · 18 revisions
  • 1.1基本类型
    • string
      • 装箱转换
    • number
      • typeof NaN => number
      • NaN === NaN => false
      • Number.isNaN()
      • 0.1 + 0.2 === 0.3 => false
      • 装箱转换
    • boolean
      • 装箱转换
    • null
      • typeof null => object
    • undefined
      • 非js关键字,取值使用void(0)代替
    • symbol
      • 装箱转换
  • 1.2引用类型
    • object
    • array
    • function

back to top

  • 2.1使用const代替var为主要变量声明方式,eslint规则:prefer-const, no-const-assign, no-var
const num = 1

const arr = []
arr.push(123, 456)
  • 2.2部分需重新分配引用的变量使用let声明
const end = 6
let num = 0
while(num < end) {
    // your code
    num++
}
  • 2.3const, let声明变量存在块级作用域,且不会成为window属性(浏览器宿主环境)
{
    let o1 = 1
    const o2 = 2
}
console.log(o1) // ReferenceError
console.log(o2) // ReferenceError

const a = 1
let b = 2
console.log(window.a) // undefined
console.log(window.b) // undefined
  • 2.4var声明变量为函数作用域,会成为window属性(浏览器宿主环境)

back to top

  • 3.1使用对象字面量{}方式声明对象,而非使用new Object()方式,eslint规则no-new-object
  • 3.2使用属性简洁表示法定义对象,简洁且描述性更好
const name = 'jack'

// bad
const obj = {
    name: name,
    getName: function (value) {
        return obj.name + value
    }
}

// good
const obj = {
    name,
    getName(value) {
        return obj.name + value
    }
}
  • 3.3使用属性简洁表示法的属性应在对象字面量开头定义,便于区分
const name = 'jack'
const age = 18

//  bad
const obj = {
    id: 1,
    name,
    sex: 'male',
    age
}

// good
const obj = {
    name,
    age,
    sex: 'male',
    id: 1
}
  • 3.4使用属性名表达式表示对象变化的属性名,便于在字面量声明内定义对象所有属性
function getKey(k) {
    return `_${k}`
}

// bad
const obj = {
    name: 'jack',
    age: 18
}
obj[getKey('id')] = 1

// good
const obj = {
    [getKey('id')]: 1,
    name: 'jack',
    age: 18
}
  • 3.5仅当对象属性名为无效标识符时才使用引号属性,便于js引擎解析代码,代码易读及编译器语法高亮显示,eslint规则quote-props
// bad
const obj = {
    'name': 'jack',
    'age': 18,
    'first-name': 'xu'
}

// good
const obj = {
    name: 'jack',
    age: 18,
    'first-name': 'xu'
}
  • 3.6对象不宜直接调用其原型对象Object.prototype上的方法,如:hasOwnProperty, propertyIsEnumerable, isPrototypeOf,防止这些方法被对象的同名方法覆盖。eslint规则no-prototype-builtins
const obj = {
    name: 'jack'
}

// bad
console.log(obj.hasOwnProperty('name'))

// good
console.log(Object.prototype.hasOwnProperty.call(obj, 'name'))

// best
const has = Object.prototype.hasOwnProperty
console.log(has.call(obj, 'name'))
  • 3.7使用扩展运算符...代替Object.assign浅拷贝对象,使用rest参数拷贝对象部分属性。
// very bad
const origin = {
    a: 1,
    b: 2
}
const copy = Object.assign(origin, {c: 3}) // origin已变化, {a: 1, b: 2, c: 3}
delete copy.a // origin再次变化, {b: 2, c: 3}

// bad
const origin = {
    a: 1,
    b: 2
}
const copy = Object.assign({}, origin, { c: 3 })
// orign: {a:1, b: 2}
// cpoy:  {a: 1, b: 2, c: 3}


// good
const origin = {
    a: 1,
    b: 2
}
const copy = { ...origin, c: 3 } 
// { a: 1, b: 2, c: 3 }

const { a, ...noA } = copy
// { b: 2, c: 3 }

back to top

  • 4.1使用数组字面量[]方式声明数组,而非使用new Array()方式,eslint规则no-array-constructor
  • 4.2使用push往数组添加元素,而非使用直接赋值方式
const arr = []

// bad 
arr[arr.length] = 1

// good
arr.push(1)
  • 4.3使用扩展运算符...复制数组
const arr = [1, 2, 3]

// bad
const copy = []
let i = 0
const len = arr.length

while(i < len) {
    copy[i] = arr[i]
    i++
}

// good
const copy = [...arr]
  • 4.4使用扩展运算符...将可遍历对象(部署Iterator接口)转换为数组
const set = new Set([1, 2, 3])

// good
const list = Array.from(set)

// best
const list = [...set]
  • 4.5使用Array.from将类数组对象转换为数组
const arrLike = { 0: 'foo', 1: 'bar', 2: 'baz', length: 3 }

// bad
const arr = Array.prototype.slice.call(arrLike)

// good
const arr = Array.from(arrLike)
  • 4.6当映射可遍历对象时,使用Array.from代替...,避免多创建一个临时[]
const set = new Set([1, 2, 3])

// bad
const list = [...set].map(item => item + 1)

// best
const list = Array.from(set, item => item + 1)
  • 4.7在数组方法回调函数中使用return语句, 如果函数体由一个返回表达式的语句组成,并且没有副作用,则可以省略返回。eslint规则:array-callback-return
// good
[1,2,3].map(x => {
    const y = x + 2
    return x * y - 1
})

const arr = [
    {
        x: '1月',
        y: 50
    },
    {
        x: '2月',
        y: 200
    }
]

// bad
const arr2 = arr.filter(item => {
    let { y } = item;
    if (Number(y) || y === 0) {
        return y > 50
    } else {
        return false
    }
})

// good
const arr2 = arr.filter(item => {
    let { y } = item;
    if (Number(y) || y === 0) {
        return y > 50
    }
    return false
})

// bad
const flatArr = [[0, 1], [2, 3], [4, 5]].reduce((acc, curr) => {
    const flatten = acc.concat(curr)
})

// good
const flatArr = [[0, 1], [2, 3], [4, 5]].reduce((acc, curr) => {
    const flatten = acc.concat(curr)
    return flatten
})

  • 4.8数组包含多个元素时,元素之间使用换行书写风格
// bad 
const arr1 = [
    [1, 2], [3, 4], [5, 6]
]

const arr2 = [{
    id: 1,
    name: 'jj'
}, {
    id: 2,
    name: 'jdjd'

}]

const arr3 = [
    2, 3, 4
]

// good
const arr1 = [
    [1, 2],
    [3, 4],
    [5, 6]
]

const arr2 = [
    {
        id: 1,
        name: 'jj'
    },
    {
        id: 2,
        name: 'jdjd'

    }
]

const arr3 = [
    2,
    3,
    4
]

back to top

  • 5.1当访问某对象的多个属性时应使用解构赋值方式,因为解构赋值能避免为这些属性创建临时引用。eslint规则:prefer-destructuring
// bad
function getFullName(user) {
  const firstName = user.firstName;
  const lastName = user.lastName;

  return `${firstName} ${lastName}`;
}

// good
function getFullName(user) {
  const { firstName, lastName } = user;
  return `${firstName} ${lastName}`;
}

// best
function getFullName({ firstName, lastName }) {
  return `${firstName} ${lastName}`;
}

  • 5.2使用数组解构赋值方式获取数组元素,eslint规则:prefer-destructuring
const arr = [1, 2, 3, 4];

// bad
const first = arr[0];
const second = arr[1];

// good
const [first, second] = arr;
  • 5.3访问函数返回值的方式尽可能使用对象解构赋值代替数组解构赋值,因为数组解构赋值方式不便于扩展返回的对象,且访问有属性在数组中顺序的限制。
// bad
function processInput(input) {
  return [left, right, top, bottom];
}

// 访问属性需要考虑属性的顺序
const [left, __, top] = processInput(input);

// good
function processInput(input) {
  return { left, right, top, bottom };
}

// 访问属性只需考虑需要的属性
const { left, top } = processInput(input);

back to top

  • 6.1使用单引号定义字符串,eslint规则:quotes
// bad
const name = "Capt. Janeway";

// bad - 模板字符串应包含换行或插值
const name = `Capt. Janeway`;

// good
const name = 'Capt. Janeway';
  • 6.2字符串与变量连接时,使用模板字符串方式,因为模板字符串语法简洁、可读性好且支持适当的换行和字符串插值功能。eslint规则:prefer-template template-curly-spacing
// 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}?`;
}
  • 6.3不使用eval()来解析字符串,漏洞较多。 eslint规则: no-eval
  • 6.4尽量避免不必要的字符串转义,因为不必要\的可读性较差,必要时才添加。eslint规则:no-useless-escape
// bad
const foo = '\'this\' \i\s \"quoted\"';

// good
const foo = '\'this\' is "quoted"';
const foo = `my name is '${name}'`;

back to top

  • 7.1使用函数字面量方式声明函数,而非new Function方式。eslint规则:no-new-func
  • 7.2多个参数的函数宜将每个参数单独放一行,最后一项后面加,,eslint规则:function-paren-newline
// bad
function foo(bar,
             baz,
             quux) {
  // ...
}

// good
function foo(
  bar,
  baz,
  quux,
) {
  // ...
}

// bad
console.log(foo,
  bar,
  baz);

// good
console.log(
  foo,
  bar,
  baz,
);
  • 7.3尽量以扩展运算符...代替apply的方式调用函数,这样不需要提供一个函数调用上下文,且apply调用函数方式不宜把控。eslint规则:prefer-spread
// bad
Math.max.apply(null,[1,2,3,6])

// good
Math.max(...[1,2,3,6])

// bad
new (Function.prototype.bind.apply(Date, [null, 2016, 8, 5]));

// good
new Date(...[2016, 8, 5]);

  • 7.4函数参数不应被再次赋值(分配),重新赋值参数可能导致意外行为发生,特别是重新分配arguments对象,eslint规则:no-param-reassign
// bad
function f1(a) {
  a = 1;
  // ...
}

function f2(a) {
  if (!a) { a = 1; }
  // ...
}

// good
function f3(a) {
  const b = a || 1;
  // ...
}

function f4(a = 1) {
  // ...
}
  • 7.5不改变函数参数对象,因为操作函数参数传入的对象可能会在原始调用程序中造成不必要的变量副作用。eslint规则:no-param-reassign
// bad
function f1(obj) {
  obj.key = 1;
}

// good
function f2(obj) {
  const key = Object.prototype.hasOwnProperty.call(obj, 'key') ? obj.key : 1;
}
  • 7.6函数签名间保留空格,便于函数名修改时不必要人为添加删除空格。eslint规则:space-before-function-paren space-before-blocks
// bad
const f = function(){};
const g = function (){};
const h = function() {};

// good
const x = function () {};
const y = function a() {};
  • 7.7带默认值的函数参数应为最后一个参数。
// bad
function handleThings(opts = {}, name) {
  // ...
}

// good
function handleThings(name, opts = {}) {
  // ...
}
  • 7.8注意函数默认参数副作用。
var b = 1;
// bad
function count(a = b++) {
  console.log(a);
}
count();  // 1
count();  // 2
count(3); // 3
count();  // 3
  • 7.9使用函数参数默认值代替改变函数参数对象。
// really bad
function handleThings(opts) {
  // No! We shouldn’t mutate function arguments.
  // Double bad: if opts is falsy it'll be set to an object which may
  // be what you want but it can introduce subtle bugs.
  opts = opts || {};
  // ...
}

// still bad
function handleThings(opts) {
  if (opts === void 0) {
    opts = {};
  }
  // ...
}

// good
function handleThings(opts = {}) {
  // ...
}
  • 7.10函数参数不应命名为arguments, 因为命名为arguments的参数将会覆盖原函数作用域内的arguments对象。
// bad
function foo(name, options, arguments) {
  // ...
}

// good
function foo(name, options, args) {
  // ...
}
  • 7.11使用rest参数的方式代替操作arguments对象获取函数参数,因为rest语法明确你要提取参数,且总是返回数组,而非Array-like。eslint规则:prefer-rest-params
// bad
function concatenateAll() {
  const args = Array.prototype.slice.call(arguments);
  return args.join('');
}

// good
function concatenateAll(...args) {
  return args.join('');
}
  • 7.12不要在非函数作用域中(if, while, {}etc)直接声明函数,虽然浏览器允许这种声明方式,但不用浏览器对这种声明方式解析不一致。若要声明应使用将函数赋值给变量的方式声明,eslint规则:no-loop-func
// bad
if (currentUser) {
  function test() {
    console.log('Nope.');
  }
}

// good
let test;
if (currentUser) {
  test = () => {
    console.log('Yup.');
  };
}
  • 7.13使用声明函数表达式的方式代替直接函数声明,eslint规则:func-style

函数声明存在提升,这意味着在定义函数之前很容易引用它,容易造成可读性和可维护性较差

// bad
function foo() {
  // ...
}

// bad
const foo = function () {
  // ...
};

// good
// 函数名与引用函数的变量名使用不用名称
const short = function longUniqueMoreDescriptiveLexicalFoo() {
  // ...
};

back to top

  • 8.1当必须使用匿名函数(回调函数)时,尽量使用箭头函数。eslint规则:prefer-arrow-callback, arrow-spacing

因为箭头函数语法简洁,且this绑定为定义时所在的作用域,而不是指向运行时所在的作用域,这通常可能是你期望的结果。

// 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;
});
  • 8.2如果箭头函数函数体仅由一个语句组成,且该语句返回一个没有副作用的表达式,则省略大括号并使用隐式返回。反之,则应保留大括号并使用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 + 1}.`);

// 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 effects
function foo(callback) {
  const val = callback();
  if (val === true) {
    // Do something if callback returns true
  }
}

let bool = false;

// bad
foo(() => bool = true);

// good
foo(() => {
  bool = true;
});
  • 8.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,
  )
));
  • 8.4始终在函数参数周围加上(),以保持清晰和一致性, 且易于添加或删除函数参数。eslint规则:arrow-parens
// bad
[1, 2, 3].map(x => {
  const y = x + 1;
  return x * y;
});

// good
[1, 2, 3].map((x) => {
  const y = x + 1;
  return x * y;
});
  • 8.5避免比较运算符<=, >=与箭头函数标识符=>混淆,eslint规则:no-confusing-arrow
// bad
const itemHeight = (item) => item.height <= 256 ? item.largeSize : item.smallSize;

// bad
const itemHeight = (item) => item.height >= 256 ? item.largeSize : item.smallSize;

// good
const itemHeight = (item) => (item.height <= 256 ? item.largeSize : item.smallSize);

// good
const itemHeight = (item) => {
  const { height, largeSize, smallSize } = item;
  return height <= 256 ? largeSize : smallSize;
};
  • 8.6使用隐式返回的箭头函数,函数体不应换行。eslint规则:implicit-arrow-linebreak
// bad
(foo) =>
  bar;

(foo) =>
  (bar);

// good
(foo) => bar;
(foo) => (bar);
(foo) => (
   bar
)

back to top

  • 9.1使用class语法代替直接操作原型prototype,因为class语法更简洁,更容易推理。
// bad
function Queue(contents = []) {
  this.queue = [...contents];
}
Queue.prototype.pop = function () {
  const value = this.queue[0];
  this.queue.splice(0, 1);
  return value;
};

// good
class Queue {
  constructor(contents = []) {
    this.queue = [...contents];
  }
  pop() {
    const value = this.queue[0];
    this.queue.splice(0, 1);
    return value;
  }
}
  • 9.2使用extends继承,因为它是一种不破坏instanceof机制的内置继承方法。
// bad
function Queue(contents = []) {
  this.queue = [...contents];
}
Queue.prototype.pop = function () {
  const value = this.queue[0];
  this.queue.splice(0, 1);
  return value;
};

function PeekableQueue(contents) {
  Queue.apply(this, contents);
}
PeekableQueue.prototype.peek = function () {
  return this.queue[0];
};

// good
class Queue {
  constructor(contents = []) {
    this.queue = [...contents];
  }
  pop() {
    const value = this.queue[0];
    this.queue.splice(0, 1);
    return value;
  }
}

class PeekableQueue extends Queue {
  peek() {
    return this.queue[0];
  }
}

  • 9.3方法可以通过返回this来进行链式调用。
// bad
function Jedi() { }
Jedi.prototype.jump = function () {
  this.jumping = true;
  return true;
};

Jedi.prototype.setHeight = function (height) {
  this.height = height;
};

const luke = new Jedi();
luke.jump(); // => true
luke.setHeight(20); // => undefined

// good
class Jedi {
  jump() {
    this.jumping = true;
    return this;
  }

  setHeight(height) {
    this.height = height;
    return this;
  }
}

const luke = new Jedi();

luke.jump()
  .setHeight(20);
  • 9.4类可以编写一个自定义的toString()方法,但要确保它“成功工作”,且不会产生副作用。
class Jedi {
  constructor(options = {}) {
    this.name = options.name || 'no name';
  }

  getName() {
    return this.name;
  }

  toString() {
    return `Jedi - ${this.getName()}`;
  }
}
  • 9.5如果未指定类构造函数,代码解析时会默认一个构造函数。人为指定一个空构造函数或指定一个仅是代理父类的构造函数是不必要的。eslint规则:no-useless-constructor
// bad
class A {
    constructor () {
    }
}

class B extends A {
    constructor (...args) {
      super(...args);
    }
}

// good
class A { }

class A {
    constructor () {
        doSomething();
    }
}

class B extends A {
    constructor() {
        super('foo');
    }
}

class B extends A {
    constructor() {
        super();
        doSomething();
    }
}
  • 9.6避免重复类成员,重复类成员会取最后一个生效,属于重复声明bug。eslint规则:no-dupe-class-members
// bad
class Foo {
  bar() { return 1; }
  bar() { return 2; }
}

// good
class Foo {
  bar() { return 1; }
}

// good
class Foo {
  bar() { return 2; }
}

back to top

  • 10.1尽量使用es6模块import/export代替非标准的模块系统。
// bad
const AirbnbStyleGuide = require('./AirbnbStyleGuide');
module.exports = AirbnbStyleGuide.es6;

// ok
import AirbnbStyleGuide from './AirbnbStyleGuide';
export default AirbnbStyleGuide.es6;

// best
import { es6 } from './AirbnbStyleGuide';
export default es6;
  • 10.2尽量不使用*整体导入模块,这样可确保只有一个默认导出。
// bad
import * as AirbnbStyleGuide from './AirbnbStyleGuide';

// good
import AirbnbStyleGuide from './AirbnbStyleGuide';
  • 10.3不使用exportimport语句结合在一起,写成一行。虽然写成一行代码简洁,但有明确的导入和导出方式的写法,可以使事情保持一致,代码更易读。
// bad
// filename es6.js
export { es6 as default } from './AirbnbStyleGuide';

// bad
export { foo, bar } from 'my_module';

// good
// filename es6.js
import { es6 } from './AirbnbStyleGuide';
export default es6;
  • 10.4多个模块只从唯一路径一次导入,从同一路径导入多次会使代码更难维护。eslint规则:no-duplicate-imports
// bad
import foo from 'foo';
// … some other imports … //
import { named1, named2 } from 'foo';

// good
import foo, { named1, named2 } from 'foo';

// good
import foo, {
  named1,
  named2,
} from 'foo';
  • 10.5不导出可变的绑定,一般情况下模块导出应避免冲突,尤其是导出可变的绑定时更应注意。虽然在某些特殊情况下可能需要导出可变绑定,但大多数情况,应只导出常量引用。eslint规则:import/no-mutable-exports
// bad
let foo = 3;
export { foo };

// good
const foo = 3;
export { foo };
  • 10.6只有一个导出的模块中,宜使用export default代替export。eslint规则:import/prefer-default-export

建议一个模块只有一个导出,这样代码的可读性和可维护性更好。

// bad
export function foo() {}

// good
export default function foo() {}
  • 10.7将所有import导入放在非导入语句之前。eslint规则:import/first
// bad
import foo from 'foo';
foo.init();

import bar from 'bar';

// good
import foo from 'foo';
import bar from 'bar';

foo.init();
  • 10.8导入多个元素时,元素之间使用换行书写风格。
// bad
import {longNameA, longNameB, longNameC, longNameD, longNameE} from 'path';

// good
import {
  longNameA,
  longNameB,
  longNameC,
  longNameD,
  longNameE,
} from 'path';
  • 10.9模块导入语句中不应该使用webpackloader语法,eslint规则:import/no-webpack-loader-syntax

因为在导入中使用webpackloader语法,会将代码耦合到模块打包,应在webpack.config.js使用loader相关配置。

// bad
import fooSass from 'css!sass!foo.scss';
import barCss from 'style!css!bar.css';

// good
import fooSass from 'foo.scss';
import barCss from 'bar.css';

back to top

  • 11.1使用.访问对象属性,eslint规则:dot-notation
const luke = {
  jedi: true,
  age: 28,
};

// bad
const isJedi = luke['jedi'];

// good
const isJedi = luke.jedi;
  • 11.2使用[]访问对象变量属性
const luke = {
  jedi: true,
  age: 28,
};

function getProp(prop) {
  return luke[prop];
}

const isJedi = getProp('jedi');
  • 11.3冥计算时,使用冥计算符**,eslint规则:no-restricted-properties
// bad
const binary = Math.pow(2, 10);

// good
const binary = 2 ** 10;

back to top

  • 12.1总是使用const、let定义变量,避免全局属性及全局命名空间产生。eslint规则:no-undef prefer-const
// bad
superPower = new SuperPower();

// good
const superPower = new SuperPower();
  • 12.2一个const、let只声明一个变量,避免同时声明多个变量,eslint规则:one-var

why?便于新增变量,不必担心,;混用出错,同时也便于每个变量debugger。

// bad
const items = getItems(),
    goSportsTeam = true,
    dragonball = 'z';

// bad
// (compare to above, and try to spot the mistake)
const items = getItems(),
    goSportsTeam = true;
    dragonball = 'z';

// good
const items = getItems();
const goSportsTeam = true;
const dragonball = 'z';
  • 12.3对所有constlet声明变量进行分组。

why?当你后一个变量声明需要引用前一个变量声明时,非常有用。

// bad
let i, len, dragonball,
    items = getItems(),
    goSportsTeam = true;

// bad
let i;
const items = getItems();
let dragonball;
const goSportsTeam = true;
let len;

// good
const goSportsTeam = true;
const items = getItems();
let dragonball;
let i;
let length;
  • 12.4在合理的位置声明你需要的变量。

why? 声明的变量为块级作用域,而非函数作用域。

// bad - unnecessary function call
function checkName(hasName) {
  const name = getName();

  if (hasName === 'test') {
    return false;
  }

  if (name === 'test') {
    this.setName('');
    return false;
  }

  return name;
}

// good
function checkName(hasName) {
  if (hasName === 'test') {
    return false;
  }

  const name = getName();

  if (name === 'test') {
    this.setName('');
    return false;
  }

  return name;
}
  • 12.5不使用=链式声明变量,eslint规则:no-multi-assign

why? 链式声明变量会创建隐式全局变量

// 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 a = b = c = 1;
}());

console.log(a); // throws ReferenceError
console.log(b); // 1
console.log(c); // 1

// good
(function example() {
  let a = 1;
  let b = a;
  let c = a;
}());

console.log(a); // throws ReferenceError
console.log(b); // throws ReferenceError
console.log(c); // throws ReferenceError

// the same applies for `const`
  • 12.6尽量避免使用++--语句,eslint规则:no-plusplus

why? 受自动;影响++--语句可能会产生非期望的自增、自减错误。同时num += 1也比num++ / num ++更直观、表达性更强。

// bad

const 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++;
  }
}

// good

const 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;
  • 12.7避免=前后出现换行,如果你的变量值过长,可在值上加(),eslint规则:operator-linebreak

why? 容易引起混淆,不便于分析代码。

// bad
const foo =
  superLongLongLongLongLongLongLongLongFunctionName();

// bad
const foo
  = 'superLongLongLongLongLongLongLongLongString';

// good
const foo = (
  superLongLongLongLongLongLongLongLongFunctionName()
);

// good
const foo = 'superLongLongLongLongLongLongLongLongString';
  • 12.8避免定义但未使用的变量,eslint规则:no-unused-vars

why? 定义但未使用的变量,可能是由于一个不完整的代码重构工作导致,这些变量可能对代码阅读者产生混淆。

// bad

var some_unused_var = 42;

// Write-only variables are not considered as used.
var y = 10;
y = 5;

// A read for a modification of itself is not considered as used.
var z = 0;
z = z + 1;

// Unused function arguments.
function getX(x, y) {
    return x;
}

// good

function getXPlusY(x, y) {
  return x + y;
}

var x = 1;
var y = a + 2;

alert(getXPlusY(x, y));

// 'type' is ignored even if unused because it has a rest property sibling.
// This is a form of extracting an object that omits the specified keys.
var { type, ...coords } = data;
// 'coords' is now the 'data' object without its 'type' property.

back to top

  • 13.1控制语句过长,或语句每个控制条件过长时,每个控制条件应换行书写,且逻辑运算符应书写在行首。

在行首书写逻辑运算符可以保持运算符对齐,遵循类似于方法链接的模式。同时也提高了代码可读性,更容易直观地阅读复杂的逻辑。

// bad
if ((foo === 123 || bar === 'abc') && doesItLookGoodWhenItBecomesThatLong() && isThisReallyHappening()) {
  thing1();
}

// bad
if (foo === 123 &&
  bar === 'abc') {
  thing1();
}

// bad
if (foo === 123
  && bar === 'abc') {
  thing1();
}

// bad
if (
  foo === 123 &&
  bar === 'abc'
) {
  thing1();
}

// good
if (
  foo === 123
  && bar === 'abc'
) {
  thing1();
}

// good
if (
  (foo === 123 || bar === 'abc')
  && doesItLookGoodWhenItBecomesThatLong()
  && isThisReallyHappening()
) {
  thing1();
}

// good
if (foo === 123 && bar === 'abc') {
  thing1();
}
  • 13.2避免使用条件运算符来代替控制语句
// bad
!isRunning && startRunning();

// good
if (!isRunning) {
  startRunning();
}

back to top

back to top

back to top