JavaScript 編碼規範

類型

基本類型

你能夠直接獲取到基本類型的值javascript

  • string
  • number
  • boolean
  • null
  • undefined
  • symbol
const foo = 1;
let bar = foo;

bar = 9;

console.log(foo, bar); // => 1, 9
注意Symbols 不能被完整的 polyfill,因此,在不支持 Symbols 的環境下中,不該該使用 symbol 類型。

複雜類型

複雜類型賦值就是獲取到他的引用的值,至關於引用傳遞css

  • object
  • array
  • function
const foo = [1, 2];
const bar = foo;

bar[0] = 9;
console.log(foo[0], bar[0]); // => 9, 9

參考

永遠都使用 const

爲了確保你不會改變你的初始值,重複引用會致使一些不可預見的 bug,還會讓代碼難以理解,全部的賦值都應該使用 const,避免使用 varhtml

eslintjava

// bad
var a = 1;
var b = 2;

// good
const a = 1;
const b = 2;

可使用 let

若是你必定要對參數從新賦值,那就使用 let,而不是 varlet 是塊級做用域,而 ver 是函數級做用域。node

eslintwebpack

// bad
var count = 1;
if (true) {
  count += 1;
}

// good
let count = 1;
if (true) {
  count += 1;
}

注意 constlet 的塊級做用域

constlet 聲明的常量與變量都只存在於定義它們的那個塊級做用域中。git

{
  let a = 1;
  const b = 1;
}

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

對象

永遠使用字面量建立對象

eslintes6

// bad
const obj = new Object();

// good
const obj = {};

使用計算屬性名

當你須要建立一個帶有 動態屬性名 的對象時,請將全部的屬性定義放在一塊兒,可使用 計算屬性名github

function getKey(key) {
  return `a key named ${key}`;
}

// bad
const obj = {
  id: 1,
  name: 'Parc MG',
};
obj[getKey('enabled')] = true;

// good
const obj = {
  id: 1,
  name: 'Parc MG',
  [getKey('enabled')]: true
};

對象方法簡寫

eslintweb

// bad
const atom = {
  value: 1,
  add: function (value) {
    return atom.value + value;
  }
};

// good
const atom = {
  value: 1,
  add(value) {
    return atom.value + value;
  }
};

屬性值縮寫

eslint

const name = 'Parc MG';

// bad
const org = {
  name: name,
};

// good
const org = {
  name,
};

將全部屬性值縮寫放在對象聲明的最前面

const name = 'Parc MG';
const url = 'https://parcmg.com';

// bad
const org = {
  email: 'contact@parcmg.com',
  name,
  created: new Date(),
  url,
};

// good
const org = {
  name,
  url,
  email: 'contact@parcmg.com',
  created: new Date(),
};

若非必要,屬性名不使用 ''

eslint

// bad
const bad = {
  'foo': 1,
  'bar': 2,
  'foo-bar': 3,
};

// good
const good = {
  foo: 1,
  bar: 2,
  'foo-bar': 3,
};

不直接調用對象原型上的方法

不直接調用一個對象的 hasOwnPropertypropertyIsEnumerableisPrototypeOf 等這些原型的方法,在某些狀況下,這些方法可能會被屏蔽掉,好比 { hasOwnProperty: false } 或者是一個空對象 Object.create(null)

// bad
obj.hasOwnProperty(key);

// good
Object.prototype.hasOwnProperty.call(obj, key);

// best
const has = Object.prototype.hasOwnProperty;

has.call(obj, key);

積極使用擴展及解構運算 ...

  • 在對象的 淺拷貝 時,更推薦使用擴展運算 { ...obj },而不是 Object.assign
  • 在獲取對象指定的幾個屬性時,使用解構運算 { foo, bar, ...rest } = obj

eslint

// very bad
const original = { a: 1, b: 2 };
const copied = Object.assign(original, { c: 3 }); // 這將致使 original 也被修改

delete copied.a; // 這樣操做以後會致使 original 也被修改
console.log(original); // => {b: 2, c: 3}

// bad
const original = { a: 1, b: 2 };
const copied = Object.assign({}, original, { c: 3}};

// good
const original = { a: 1, b: 2 };
const copied = { ...original, c: 3 };

// 解構運算與 `rest` 賦值運算
const obj = { a: 1, b: 2, c: 3 };
const { a, b } = obj; // 從對象 obj 中解構出 a, b 兩個屬性的值,並賦值給名爲 a,b 的常量
const { a, ...rest } = obj; // 從對象 obj 中解構出 a 的值,並賦值給名爲 a 的常量,同時,建立一個由全部其它屬性組成的名爲 `rest` 的新對象
console.log(rest); // => { b: 2, c: 3 }

// 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}`;
}

// the most best
const getFullName = ({ firstName, lastName }) => `${firstName} ${lastName}`;

返回多值時,使用對象解構,而非數組結構

因爲 JavaScript 不支持多值返回,當一個函數或者方法有多個值須要建立時,請爲每個值命名,並以全部值組成的對象爲單一值返回,而不是以數組的形式返回。

// 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); // 調用者能夠明確的指定須要哪一個值,並且不須要建立多餘的變量

數組

使用字面量賦值

eslint

// bad
const items = new Array();

// good
const items = [];

使用 .push 方法代替直接索引賦值

const items = [];

// bad
items[items.length] = 'new item';

// good
items.push('new item');

使用擴展運算符進行淺拷貝

const items = [1, 2, 3, 4, 5];

// bad
const length = items.length;
const copied = [];
let index;

for (index = 0; index < length; index += 1) {
  copied[index] = items[index];
}

// good
const copied = [ ...items ];

使用 ... 運算符代替 Array.from

當須要將一個可迭代的對象轉換成數組時,推薦使用 ... 操做符。

const elements = document.querySelectorAll('.foobar');

// not bad
const nodes = Array.from(elements);

// good
const nodes = [ ...elements ];

使用 ... 解構數組

const array = [1, 2, 3, 4, 5];

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

// good
const [first, second, ...rest] = array;
console.log(rest); // => [3, 4, 5]

使用 Array.from 將類數組對象轉成數組

參考: Typed Arrays
const arrayLike = { 0: 'foo', 1: 'bar', 2: 'baz', length: 3 }

// bad
const array = Array.prototype.slice.call(arrayLike);

// good
const array = Array.from(arrayLike);

使用 Array.from 對類數組對象進行遍歷

Array.from(arrayLike[, mapFn[, thisArg]]) 方法,參考 Array.from
const arrayLike = { 0: 'foo', 1: 'bar', 2: 'baz', length: 3 }

// bad
const array = [...arrayLike].map(mapFn);

// good
const array = Array.from(arrayLike, mapFn);

在數組方法的回調函數中,永遠返回正確的值

// bad - 當第一次迭代完成以後, acc 就變成了 undefined 了
[[0, 1], [2, 3], [4, 5]].reduce((acc, item, index) => {
  const flatten = acc.concat(item);
  acc[index] = flatten;
});

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

// bad
messages.filter(msg => {
  const { subject, author } = msg;
  if (subject === 'ParcMG') {
    return author === 'MG';
  } else {
    return false;
  }
});

// good
messages.filter(msg => {
  const { subject, author } = msg;
  if (subject === 'ParcMG') {
    return author === 'MG';
  }
  return false;
});

// bad
[1, 2, 3].map(x => {
  const y = x + 1;
  return x * y;
}

// good
[1, 2, 3].map(x => x * (x + 1));

一個數組有多行時,在 [] 處斷行

// bad
const array = [
  [0, 1], [2, 3], [4, 5], [6, 7]
];

const objectArray = [{
  id: 1,
}, {
  id: 2,
}];

const numberArray = [
  1, 2,
];

// good
const array = [[0, 1], [2, 3], [4, 5], [6, 7]];

const objectArray = [
  {
    id: 1,
  },
  {
    id: 2,
  }
];

const numberArray = [1, 2];

const numberArray = [
  1,
  2,
];

字符串

string 永遠使用單引號 ''

eslint

// bad
const name = "Parc M.G";

const name = `Parc M.G`;

// good
const name = 'Parc M.G';

超長的字符串,不該該使用多行串聯

// bad
const content = '《學而》是《論語》第一篇的篇名。《論語》中各篇通常都是以第\
一章的前二三個字做爲該篇的篇名。《學而》一篇包括16章,內容涉及諸多方面。其中重\
點是「吾日三省吾身」;「節用而愛人,使民以時」;「禮之用,和爲貴」以及仁、孝、\
信等道德範疇。';

const content = '《學而》是《論語》第一篇的篇名。《論語》中各篇通常都是以第' +
'一章的前二三個字做爲該篇的篇名。《學而》一篇包括16章,內容涉及諸多方面。其中重' +
'點是「吾日三省吾身」;「節用而愛人,使民以時」;「禮之用,和爲貴」以及仁、孝、' +
'信等道德範疇。';

// good
const content = '《學而》是《論語》第一篇的篇名。《論語》中各篇通常都是以第\一章的前二三個字做爲該篇的篇名。《學而》一篇包括16章,內容涉及諸多方面。其中重點是「吾日三省吾身」;「節用而愛人,使民以時」;「禮之用,和爲貴」以及仁、孝、信等道德範疇。';

使用模板而非拼接來組織可編程字符串

eslint

// bad
function hello(name) {
  return '你好,' + name + '!';
}

function hello(name) {
  return ['你好,', name, '!'].join('');
}

function hello(name) {
  return `你好,${ name }!`;
}

// good
function hello(name) {
  return `你好,${name}!`;
}

永遠不使用 eval()

eslint

若非必要,不使用轉義字符

eslint

// bad
const foo = '\'this\' \i\s \"quoted\"';

// good
const foo = '\this\' is "quoted"';

// best
const foo = `'this' is "quoted"`;

函數

使用命名函數表達式,而不是函數聲明

eslint

使用函數聲明,它的做用域會被提早,這意味着很容易在一個文件裏面引用一個還未被定義的函數,這樣大大傷害了代碼的可讀性和可維護性,若一個函數很大很複雜,那麼應該考慮將該函數單獨提取到一個文件中,抽離成一個模塊,同時不要忘記給表達式顯示的命名,這消除了由匿名函數在錯誤調用棧中產生的全部假設。
// bad
function foo() {
  // ...
}

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

// good
const foo = function foo() {
  // ...
}

// best
const foo = function longUniqueMoreDescriptiveLexicalFoo() {
  // ...
}

把當即執行函數包裹在圓括號裏

eslint

(function () {
  console.log('Welcome to the ParcMG world.');
}());

不要在非函數塊內聲明函數

雖然運行環境容許你這樣作,可是不一樣環境的解析方式不同。

eslint

//bad 
for (var i=10; i; i--) {
    (function() { return i; })();
}

while(i) {
    var a = function() { return i; };
    a();
}

do {
    function a() { return i; };
    a();
} while (i);

let foo = 0;
for (let i = 0; i < 10; ++i) {
    // Bad, `foo` is not in the loop-block's scope and `foo` is modified in/after the loop
    setTimeout(() => console.log(foo));
    foo += 1;
}

for (let i = 0; i < 10; ++i) {
    // Bad, `foo` is not in the loop-block's scope and `foo` is modified in/after the loop
    setTimeout(() => console.log(foo));
}
foo = 100;

// good
var a = function() {};

for (var i=10; i; i--) {
    a();
}

for (var i=10; i; i--) {
    var a = function() {}; // OK, no references to variables in the outer scopes.
    a();
}

for (let i=10; i; i--) {
    var a = function() { return i; }; // OK, all references are referring to block scoped variables in the loop.
    a();
}

var foo = 100;
for (let i=10; i; i--) {
    var a = function() { return foo; }; // OK, all references are referring to never modified variables.
    a();
}

注意:在 ECMA-262 中,塊(block 的定義是:一系列語句,但函數聲明不是一個語句,命名函數表達式是一個語句。

// bad
if (currentUser) {
  function test() {
    console.log('Nope.');
  }
}

// good
let test;
if (currentUser) {
  test = () => {
    console.log('Yup.');
  };
}

不容許使用 arguments 命名參數

arguments 的優先級高於高於每一個函數做用域自帶的 arguments 對象,這會致使函數自帶的 arguments 值被覆蓋。

// bad
function foo(name, options, arguments) {
  // ...
}

// good
function foo(name, options, args) {
  // ...
}

不要在函數體內使用 arguments,使用 ...rest 代替

eslint

... 明確出你想用那個參數,同時, rest 是一個真數組,而不是一個類數組的 arguments
// bad
function concatenateAll() {
  const args = Array.prototype.slice.call(arguments);
  return args.join('');
}

// good
function concatenateAll(...args) {
  return args.join('');
}

使用默認參數,而不是在函數體內對參數從新賦值

// really bad
function handleThings(options) {
  options = options || {};
}

// still bad
function handleTings(options) {
  if (options === void 0) {
    options = {};
  }
}

// good
function handleThings(options = {}) {
}

默認參數要避免反作用

// bad
let v = 1;
const count = function count(a = v++) {
  console.log(a);
}

count();   // => 1
count();   // => 2
count(3);  // => 3
count();   // => 3

// maybe
const v = 1;
const count = function count(a = v) {
  console.log(a);
}

把默認參數放在最後

// bad
function handleTings(options = {}, name) {
  // ...
}

// good
function handleTings(name, options = {}) {
  // ...
}

不要使用函數構造器構造函數

eslint

// bad
var add = new Function('a', 'b', 'return a + b');

// still bad
var subtract = Function('a', 'b', 'return a - b');

// good
const subtract = (a, b) => a + b;

函數簽名部分要有空格

eslint

// bad
const f = function(){};
const g = function (){};
const h = function() {};

// good
const f = function a() {};

不修改參數

eslint

函數簽名時定義的參數,在函數體內不容許被從新賦值(包含參數自己,若參數爲對象,還包括該對象全部屬性的值),
一個函數應該是沒有任何反作用的。
// bad
function f1 (obj) {
  obj.key = 1;
};

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

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

// good
function f4(obj) {
  const key = Object.prototype.hasOwnProperty.call(obj, 'key') ? obj.key : 1;
};

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

function f6(a = 1) {
  // ...
}

使用 spread 操做符 ... 調用多變參數函數

eslint

// bad
const x = [1, 2, 3, 4, 5];
console.log.apply(console, x);

// good
const x = [1, 2, 3, 4, 5];
console.log(...x);

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

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

若函數簽名包含多個參數須要使用多行,那就每行有且僅有一個參數

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

// good 縮進不要太過度
function foo(
  bar,
  baz,
  quux,
) {
  // ...
}

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

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

箭頭函數

當你必定要用函數表達式的時候,就使用箭頭函數

eslint

// 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;
});

若是函數體有且僅有一個沒有反作用的表達式,那麼刪除大括號和 return

eslint

// 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
}));

// 表達式有反作用就不要用隱式return
function foo(callback) {
  const val = callback();
  if (val === true) {
    // Do something if callback returns true
  }
}

let bool = false;

// bad
// 這種狀況會return bool = true, 很差
foo(() => bool = true);

// good
foo(() => {
  bool = true;
});

若表達式包含多行,用圓括號包裹起來

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

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

若函數只有一個參數,且沒有大括號,那就刪除圓括號,不然,參數老是放在圓括號裏。

eslint

// bad
[1, 2, 3].map((x) => x * x);

// good
[1, 2, 3].map(x => x * x);

// good
[1, 2, 3].map(number => (
  `A long string with the ${number}. It’s so long that we don’t want it to take up space on the .map line!`
));

// 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;
});

避免箭頭函數(=>)和比較操做符(<=, >=)混淆.

eslint

// 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;
};

在隱式return中強制約束函數體的位置, 就寫在箭頭後面

eslint

// bad
(foo) =>
  bar;

(foo) =>
  (bar);

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

類與構造器

使用構造器,而不是 prototype

// 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;
  }
}

使用 extends 實現繼承

它是一種內置的方法來繼承原型功能而不打破 instanceof

// bad
const inherits = require('inherits');
function PeekableQueue(contents) {
  Queue.apply(this, contents);
}
inherits(PeekableQueue, Queue);
PeekableQueue.prototype.peek = function () {
  return this._queue[0];
}

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

方法能夠返回 this 實現方法鏈

// bad
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);

只要保證能夠正常工做且沒有反作用,能夠自已定製 toString 方法

class Jedi {
  constructor(options = {}) {
    this.name = options.name || 'no name';
  }

  getName() {
    return this.name;
  }

  toString() {
    return `Jedi - ${this.getName()}`;
  }
}

不要寫無用的構造函數

eslint

// bad
class Jedi {
  constructor() {}

  getName() {
    return this.name;
  }
}

// bad
class Rey extends Jedi {
  // 這種構造函數是不須要寫的
  constructor(...args) {
    super(...args);
  }
}

// good
class Rey extends Jedi {
  constructor(...args) {
    super(...args);
    this.name = 'Rey';
  }
}

避免重複類成員

eslint

// bad
class Foo {
  bar() { return 1; }
  bar() { return 2; }
}

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

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

模塊

使用 import / export

// bad
const Button = require('./Button');
module.exports = Button.es6;

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

// best
import { es6 } from './Button';
export default es6;

不要 import 通配符

// bad
import * as Component from './Component';

// good
import Component from './Component';

不要直接從 importexport

雖然一行是簡潔的,有一個明確的方式進口和一個明確的出口方式來保證一致性。
// bad
export { es6 as default } from './Component';

// good
import { es6 } from './Component';
export default es6;

一個路徑只 import 一次

eslint

從同一個路徑下import多行會使代碼難以維護
// 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';

若非必要,不要 export 可變量

eslint

變化一般都是須要避免,特別是當你要輸出可變的綁定。雖然在某些場景下可能須要這種技術,但總的來講應該導出常量。
// bad
let foo = 3;
export { foo }

// good
const foo = 3;
export { foo }

在一個單一導出模塊裏,使用 export default

eslint

鼓勵使用更多文件,每一個文件只作一件事情並導出,這樣可讀性和可維護性更好。
// bad
export function foo() {}

// good
export default function foo() {}

import 應該放在全部其它語句以前

eslint

// bad
import foo from 'foo';
foo.init();

import bar from 'bar';

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

foo.init();

多行import應該縮進,就像多行數組和對象字面量

花括號與樣式指南中每一個其餘花括號塊遵循相同的縮進規則,逗號也是。
// bad
import {longNameA, longNameB, longNameC, longNameD, longNameE} from 'path';

// good
import {
  longNameA,
  longNameB,
  longNameC,
  longNameD,
  longNameE,
} from 'path';

若使用 webpack,不容許在 import 中使用 webpack loader 語法

eslint

一旦用 Webpack 語法在 import 裏會把代碼耦合到模塊綁定器。最好是在 webpack.config.js 裏寫 webpack 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';

迭代器與生成器

不要使用遍歷器

eslint

用JavaScript高級函數代替 for-infor-of

  • 這強調了咱們不可變的規則。 處理返回值的純函數比反作用更容易。
  • 用數組的這些迭代方法: map()every()filter()find()findIndex()reduce()some()……
  • 用對象的這些方法 Object.keys()Object.values()Object.entries 去產生一個數組, 這樣你就能去遍歷對象了。
const numbers = [1, 2, 3, 4, 5];

// bad
let sum = 0;
for (let num of numbers) {
  sum += num;
}
sum === 15;

// good
let sum = 0;
numbers.forEach(num => sum += num);
sum === 15;

// best (use the functional force)
const sum = numbers.reduce((total, num) => total + num, 0);
sum === 15;

// bad
const increasedByOne = [];
for (let i = 0; i < numbers.length; i++) {
  increasedByOne.push(numbers[i] + 1);
}

// good
const increasedByOne = [];
numbers.forEach(num => increasedByOne.push(num + 1));

// best (keeping it functional)
const increasedByOne = numbers.map(num => num + 1);

不要用 generator

eslint

它在es5上支持的很差

若是必定要用,那麼必定須要注意一點:function* 是同一律念關鍵字, * 並非 function 的修飾符, function* 是一個與 function 徹底不同的獨特結構。

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

// bad
const bar = function * () {
  // ...
}

// bad
const baz = function *() {
  // ...
}

// bad
const quux = function*() {
  // ...
}

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

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

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

// very bad
const wat = function
*
() {
  // ...
}

// good
function* foo() {
  // ...
}

// good
const foo = function* () {
  // ...
}

屬性

訪問屬性使用 .

eslint

這條,涉及一個曾經阿里出過一個看似簡單,實則很難的面試題,你就算猜對一個,你也不必定能說出原理:

a.b.c.d和a'b'['d'],哪一個性能更高

到這裏,忽然想起這個梗,有興趣的能夠翻看一下 這裏

const luke = {
  jedi: true,
  age: 28,
};

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

// good
const isJedi = luke.jedi;

當獲取屬性名稱自己是一個變量是,使用 [] 訪問

const luke = {
  jedi: true,
  age: 28,
};

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

const isJedi = getProp('jedi');

冪等使用 ** 操做符

eslint

// bad
const binary = Math.pow(2, 10);

// good
const binary = 2 ** 10;

變量

永遠使用 const 或者 let,不使用 var

eslint

// bad
superPower = new SuperPower();

// good
const superPower = new SuperPower();

每個變量都用一個 const 或者 let

eslint

扯蛋的理由:這種方式很容易去聲明新的變量,你不用去考慮把;調換成,,或者引入一個只有標點的不一樣的變化。

真正的理由:作法也能夠是你在調試的時候單步每一個聲明語句,而不是一下跳過全部聲明。

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

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

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

塵歸塵,土歸土

在同一個塊中,全部的 const 放在一塊兒,全部的 let 放在一塊兒

// 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;

在你須要的地方聲明變量,但要放在合理的位置

// bad
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;
}

不使用連接變量分配

eslint

連接變量分配會隱匿建立全局變量

// bad
(function example() {
  // JavaScript 將這一段解釋爲
  // let a = ( b = ( c = 1 ) );
  // let 只對變量 a 起做用; 變量 b 和 c 都變成了全局變量
  let a = b = c = 1;
}());

console.log(a); // undefined
console.log(b); // 1
console.log(c); // 1

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

console.log(a); // undefined
console.log(b); // undefined
console.log(c); // undefined

// `const` 也是如此

不使用一元自增自減運算(++--

eslint

根據 eslint 文檔,一元增量和減量語句受到自動分號插入的影響,而且可能會致使應用程序中的值遞增或遞減的無聲錯誤。 使用 num + = 1 而不是 num ++num ++ 語句來表達你的值也是更有表現力的。 禁止一元增量和減量語句還會阻止您無心地預增/預減值,這也會致使程序出現意外行爲。

// bad

let 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

let 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;

賦值時不換行

eslint

若是賦值語句超出了 max-len 配置,那麼給值前面加上括號。

// bad
const foo =
  superLongLongLongLongLongLongLongLongFunctionName();

// bad
const foo
  = 'superLongLongLongLongLongLongLongLongString';

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

// good
const foo = 'superLongLongLongLongLongLongLongLongString';

不容許聲明不使用的變量

eslint

// bad

var some_unused_var = 42;

// 寫了沒用
var y = 10;
y = 5;

// 變量改了本身的值,也沒有用這個變量
var z = 0;
z = z + 1;

// 參數定義了但未使用
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' 即便沒有使用也能夠能夠被忽略, 由於這個有一個 rest 取值的屬性。
// 這是從對象中抽取一個忽略特殊字段的對象的一種形式
var { type, ...coords } = data;
// 'coords' 如今就是一個沒有 'type' 屬性的 'data' 對象

變量提高

永遠不要使用 var

var 聲明會將變量聲明提高到做用域的最前面,可是他的值卻只有在運行到代碼行時纔會被賦值,永遠都使用 constlet,瞭解 時效區(Temporal Dead Zones) 的相關知識,也還要知道爲何 typeof 再也不安全

// 咱們知道這個不會工做,假設沒有定義全局的notDefined
function example() {
  console.log(notDefined); // => throws a ReferenceError
}

// 在你引用的地方以後聲明一個變量,他會正常輸出是由於變量做用域上升。
// 注意: declaredButNotAssigned的值沒有上升
function example() {
  console.log(declaredButNotAssigned); // => undefined
  var declaredButNotAssigned = true;
}

// 解釋器把變量聲明提高到做用域最前面,
// 能夠重寫成以下例子, 兩者意義相同
function example() {
  let declaredButNotAssigned;
  console.log(declaredButNotAssigned); // => undefined
  declaredButNotAssigned = true;
}

// 用 const, let就不同了
function example() {
  console.log(declaredButNotAssigned); // => throws a ReferenceError
  console.log(typeof declaredButNotAssigned); // => throws a ReferenceError
  const declaredButNotAssigned = true;
}

匿名函數表達式與 var 的狀況同樣

function example() {
  console.log(anonymous); // => undefined

  anonymous(); // => TypeError anonymous is not a function

  var anonymous = function () {
    console.log('anonymous function expression');
  };
}

已命名函數表達式只提高變量名,而不是函數名或者函數體

function example() {
  console.log(named); // => undefined

  named(); // => TypeError named is not a function

  superPower(); // => ReferenceError superPower is not defined

  var named = function superPower() {
    console.log('Flying');
  };
}

// 函數名和變量名同樣是也如此
function example() {
  console.log(named); // => undefined

  named(); // => TypeError named is not a function

  var named = function named() {
    console.log('named');
  };
}

函數聲明則提高了函數名和函數體

function example() {
  superPower(); // => Flying

  function superPower() {
    console.log('Flying');
  }
}

比較操做符

永遠使用 ===!==,而不是 ==!=

eslint

if 條件語句的強制 toBoolean

if 條件語句的強制 toBoolean 老是遵循如下規則:

  • Objects 老是計算成 true
  • Undefined 老是計算 成 false
  • Null 老是計算成 false
  • Booleans 計算成它自己的布爾值
  • Numbers

    • +0-0 或者 NaN 老是計算成 false
    • 其它的所有爲 true
  • Strings

    • '' 計算成 false
    • 其它所有爲 true
注意NaN 是不等於 NaN 的,請使用 isNaN() 檢測。
if ([0] && []) {
  // true
  // 數組(即便是空數組)是對象,對象會計算成true
}

console.log(NaN === NaN) // => false
console.log(isNaN(NaN))  // => true

布爾值要使用縮寫,可是字符串與數字要明確比較對象

// bad
if (isValid === true) {
  // ...
}

// good
if (isValid) {
  // ...
}

// bad
if (name) {
  // ...
}

// good
if (name !== '') {
  // ...
}

// bad
if (collection.length) {
  // ...
}

// good
if (collection.length > 0) {
  // ...
}

switchcasedefault 分句中使用大括號建立語法聲明區域

eslint

語法聲明在整個 switch 的代碼塊裏均可見,可是隻有當其被分配後纔會初始化,他的初始化時當這個 case 被執行時才產生。 當多個 case 分句試圖定義同一個事情時就出問題了

// bad
switch (foo) {
  case 1:
    let x = 1;
    break;
  case 2:
    const y = 2;
    break;
  case 3:
    function f() {
      // ...
    }
    break;
  default:
    class C {}
}

// good
switch (foo) {
  case 1: {
    let x = 1;
    break;
  }
  case 2: {
    const y = 2;
    break;
  }
  case 3: {
    function f() {
      // ...
    }
    break;
  }
  case 4:
    bar();
    break;
  default: {
    class C {}
  }
}

三元運算符不能被嵌套

eslint

// bad
const foo = maybe1 > maybe2
  ? "bar"
  : value1 > value2 ? "baz" : null;

// better
const maybeNull = value1 > value2 ? 'baz' : null;

const foo = maybe1 > maybe2
  ? 'bar'
  : maybeNull;

// best
const maybeNull = value1 > value2 ? 'baz' : null;

const foo = maybe1 > maybe2 ? 'bar' : maybeNull;

避免沒必要要的三元表達式

// bad
const foo = a ? a : b;
const bar = c ? true : false;
const baz = c ? false : true;

// good
const foo = a || b;
const bar = !!c;
const baz = !c;

除非優先級顯而易見,不然使用圓括號來混合操做符

eslint

開發者須要以最顯而易見的方式明確本身的意圖與邏輯

// bad
const foo = a && b < 0 || c > 0 || d + 1 === 0;

// bad
const bar = a ** b - 5 % d;

// bad
// 別人會陷入(a || b) && c 的迷惑中
if (a || b && c) {
  return d;
}

// good
const foo = (a && b < 0) || c > 0 || (d + 1 === 0);

// good
const bar = (a ** b) - (5 % d);

// good
if (a || (b && c)) {
  return d;
}

// good
const bar = a + b / c * d;

區塊

用大括號包裹多行代碼

eslint

// bad
if (test)
  return false;

// good
if (test) return false;

// good
if (test) {
  return false;
}

// bad
function foo() { return false; }

// good
function bar() {
  return false;
}

if 以及 elseif 的關閉大括號在同一行

eslint

// bad
if (test) {
  thing1();
  thing2();
}
else {
  thing3();
}

// good
if (test) {
  thing1();
  thing2();
} else {
  thing3();
}

if 語句中的 return

eslint

  • no-else-return
  • 若是 if 語句中老是須要用 return 返回,那麼後續的 else 就不須要寫了
  • 若是 if 塊中包含 return,它後面的 else if 也包含了 return,那就應該把 else ifreturn 分到多個 if 語句塊中去。
// bad
function foo() {
  if (x) {
    return x;
  } else {
    return y;
  }
}

// bad
function cats() {
  if (x) {
    return x;
  } else if (y) {
    return y;
  }
}

// bad
function dogs() {
  if (x) {
    return x;
  } else {
    if (y) {
      return y;
    }
  }
}

// good
function foo() {
  if (x) {
    return x;
  }

  return y;
}

// good
function cats() {
  if (x) {
    return x;
  }

  if (y) {
    return y;
  }
}

// good
function dogs(x) {
  if (x) {
    if (z) {
      return y;
    }
  } else {
    return z;
  }
}

控制語句

當你的控制語句 (ifwhile)等太長,或者超過最大長度限制時,把每個判斷條件都放到單獨一行去,邏輯操做符放在行首

// 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();
}

不要用選擇操做符代替控制語句

// bad
!isRunning && startRunning();

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

註釋

多行註釋使用 /** ... */

// bad
// make() 基於傳入的 `tag` 名返回一個新元素
//
// @param {String} 標籤名
// @return {Element} 新元素
function make(tag) {

  // ...

  return element;
}

// good
/**
 * make() 基於傳入的 `tag` 名返回一個新元素
 * @param {String} 標籤名
 * @param {Element} 新元素
 */
function make(tag) {

  // ...

  return element;
}

單行註釋用 //

將單行註釋放在被註釋區域上面。若是註釋不是在第一行,那麼註釋前面就空一行

// bad
const active = true;  // is current tab

// good
// 當前激活狀態的 tab
const active = true;

// bad
function getType() {
  console.log('fetching type...');
  // 設置默認 `type` 爲 'no type'
  const type = this._type || 'no type';

  return type;
}

// good
function getType() {
  console.log('fetching type...');

  // 設置默認 `type` 爲 'no type'
  const type = this._type || 'no type';

  return type;
}

// also good
function getType() {
  // 設置默認 `type` 爲 'no type'
  const type = this._type || 'no type';

  return type;
}

全部註釋開頭空一個,方便閱讀

eslint

// bad
//當前激活的 tab
const active = true;

// good
// 當前激活的 tab
const active = true;

// bad
/**
 *make() 基於傳入的 `tag` 名返回一個新元素
 *@param {String} 標籤名
 *@param {Element} 新元素
 */
function make(tag) {

  // ...

  return element;
}

// good
/**
 * make() 基於傳入的 `tag` 名返回一個新元素
 * @param {String} 標籤名
 * @param {Element} 新元素
 */
function make(tag) {

  // ...

  return element;
}

積極使用 FIXMETODO

當你的註釋須要向註釋閱讀者或者代碼的後繼開發者明確的表述一種指望時,應該積極使用 FIXME 以及 TODO 前綴,這有助於其餘的開發理解你指出的須要從新訪問的問題,也方便本身往後有時間的時候再次回顧當時沒有解決或者計劃去作而沒有作的事情。

  • FIXME:這裏有一個問題,如今尚未影響大局,可是更但願解決這個問題或者找到一個更優雅的方式去實現
  • TODO:計劃在這裏去實現某些功能,如今尚未實現
// 使用 FIXME: 
class Calculator extends Abacus {
  constructor() {
    super();

    // FIXME: 不該該在此處使用全局變量
    total = 0;
  }
}

// 使用 TODO: 
class Calculator extends Abacus {
  constructor() {
    super();

    // TODO: total 應該應該從一個參數中獲取並初始化
    this.total = 0;
  }
}

空格

代碼縮進老是使用兩個空格

eslint

// bad
function foo() {
∙∙∙∙const name;
}

// bad
function bar() {
∙const name;
}

// good
function baz() {
∙∙const name;
}

在大括號前空一格

eslint

// bad
function test(){
  console.log('test');
}

// good
function test() {
  console.log('test');
}

// bad
dog.set('attr',{
  age: '1 year',
  breed: 'Bernese Mountain Dog',
});

// good
dog.set('attr', {
  age: '1 year',
  breed: 'Bernese Mountain Dog',
});

關鍵字空格

eslint

在控制語句( if, while 等)的圓括號前空一格。在函數調用和定義時,參數列表和函數名之間不空格。

// bad
if(isJedi) {
  fight ();
}

// good
if (isJedi) {
  fight();
}

// bad
function fight () {
  console.log ('Swooosh!');
}

// good
function fight() {
  console.log('Swooosh!');
}

用空格來隔開運算符

eslint

// bad
const x=y+5;

// good
const x = y + 5;

文件結尾加一個換行

eslint

// bad
function doSmth() {
  var foo = 2;
}
// good
function doSmth() {
  var foo = 2;
}\n

使用多行縮進的方式進行一個長方法鏈調用

eslint

// 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();

// bad
const 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);

// good
const 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);

// good
const leds = stage.selectAll('.led').data(data);

在一個代碼塊後下一條語句前空一行

// bad
if (foo) {
  return bar;
}
return baz;

// good
if (foo) {
  return bar;
}

return baz;

// bad
const obj = {
  foo() {
  },
  bar() {
  },
};
return obj;

// good
const obj = {
  foo() {
  },

  bar() {
  },
};

return obj;

// bad
const arr = [
  function foo() {
  },
  function bar() {
  },
];
return arr;

// good
const arr = [
  function foo() {
  },

  function bar() {
  },
];

return arr;

不要用空白行填充塊

eslint

// bad
function bar() {

  console.log(foo);

}

// also bad
if (baz) {

  console.log(qux);
} else {
  console.log(foo);

}

// good
function bar() {
  console.log(foo);
}

// good
if (baz) {
  console.log(qux);
} else {
  console.log(foo);
}

圓括號裏不加空格

eslint

// bad
function bar( foo ) {
  return foo;
}

// good
function bar(foo) {
  return foo;
}

// bad
if ( foo ) {
  console.log(foo);
}

// good
if (foo) {
  console.log(foo);
}

方括號裏,首尾都不要加空格與元素分隔

eslint

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

// good, 逗號分隔符仍是要空格的
const foo = [1, 2, 3];
console.log(foo[0]);

花括號裏要加空格

eslint

// bad
const foo = {clark: 'kent'};

// good
const foo = { clark: 'kent' };

避免一行代碼超過100個字符(包含空格)

eslint

爲了確保代碼的人類可讀性與可維護性,代碼行應避免超過必定的長度

// bad
const 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://parcmg.com/', data: { name: 'John' } }).done(() => console.log('Congratulations!')).fail(() => console.log('You have failed this city.'));

// good
const 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://apis.parcmg.com/',
  data: { name: 'John' },
})
  .done(() => console.log('Congratulations!'))
  .fail(() => console.log('You have failed this city.'));

做爲語句的花括號裏不該該加空格

eslint

// bad
function foo() {return true;}
if (foo) { bar = 0;}

// good
function foo() { return true; }
if (foo) { bar = 0; }

, 前不要空格,,後須要空格

eslint

// bad
var foo = 1,bar = 2;
var arr = [1 , 2];

// good
var foo = 1, bar = 2;
var arr = [1, 2];

計算屬性內要空格

eslint

// bad
obj[foo ]
obj[ 'foo']
var x = {[ b ]: a}
obj[foo[ bar ]]

// good
obj[foo]
obj['foo']
var x = { [b]: a }
obj[foo[bar]]

調用函數時,函數名和小括號之間不要空格

eslint

// bad
func ();

func
();

// good
func();

在對象的字面量屬性中, keyvalue 之間要有空格

eslint

// bad
var obj = { "foo" : 42 };
var obj2 = { "foo":42 };

// good
var obj = { "foo": 42 };

行末不要空格

eslint

避免出現連續多個空行,文件末尾只容許空一行

eslint

// bad
var x = 1;



var y = 2;

// good
var x = 1;

var y = 2;

逗號

不要前置逗號

eslint

// bad
const story = [
    once
  , upon
  , aTime
];

// good
const story = [
  once,
  upon,
  aTime,
];

// bad
const hero = {
    firstName: 'Ada'
  , lastName: 'Lovelace'
  , birthYear: 1815
  , superPower: 'computers'
};

// good
const hero = {
  firstName: 'Ada',
  lastName: 'Lovelace',
  birthYear: 1815,
  superPower: 'computers',
};

額外結尾逗號

eslint

就算項目有可能運行在舊版本的瀏覽器中,可是像 Babel 這樣的轉換器都會在轉換代碼的過程當中刪除這些多餘逗號,因此,大膽使用它,徹底不會有反作用產生,相反的,他能讓咱們更方便的給對象或者多行數組增長、刪除屬性或者元素,同時,還能讓咱們的 git diffs 更清潔。
// bad - 沒有結尾逗號的 git diff
const hero = {
     firstName: 'Florence',
-    lastName: 'Nightingale'
+    lastName: 'Nightingale',
+    inventorOf: ['coxcomb chart', 'modern nursing']
};

// good - 有結尾逗號的 git diff
const hero = {
     firstName: 'Florence',
     lastName: 'Nightingale',
+    inventorOf: ['coxcomb chart', 'modern nursing'],
};
// bad
const hero = {
  firstName: 'Dana',
  lastName: 'Scully'
};

const heroes = [
  'Batman',
  'Superman'
];

// good
const hero = {
  firstName: 'Dana',
  lastName: 'Scully',
};

const heroes = [
  'Batman',
  'Superman',
];

// bad
function createHero(
  firstName,
  lastName,
  inventorOf
) {
  // does nothing
}

// good
function createHero(
  firstName,
  lastName,
  inventorOf,
) {
  // does nothing
}

// good (在一個 "rest" 元素後面,絕對不能出現逗號)
function createHero(
  firstName,
  lastName,
  inventorOf,
  ...heroArgs
) {
  // does nothing
}

// bad
createHero(
  firstName,
  lastName,
  inventorOf
);

// good
createHero(
  firstName,
  lastName,
  inventorOf,
);

// good (在一個 "rest" 元素後面,絕對不能出現逗號)
createHero(
  firstName,
  lastName,
  inventorOf,
  ...heroArgs
)

分號

永遠明確的使用分號結束你的代碼行

eslint

當 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;
}());

強類型轉換

在語句開始執行強制類型轉換

使用 String 進行字符類型轉換

eslint

// => 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);

使用 Number 進行數字類型轉換

eslint

使用 parseInt 轉換 string 一般都須要帶上基數。

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);

在註釋中說明爲何要使用移位運算

若是你感受 parseInt 知足不要你的需求,想使用移位進行運算,那麼你必定要寫明白,這是由於 性能問題,同時,你還須要注意,數字使用 64 位 表示的,但移位運算經常返回的是 32 位的整形,移位運算對於大於 32 位的整數會致使一些 意外行爲,最大的32位整數是 2,147,483,647

// good
/**
 * parseInt是代碼運行慢的緣由
 * 用Bitshifting將字符串轉成數字使代碼運行效率大幅增加
 */
const val = inputValue >> 0;

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

布爾

const age = 0;

// bad
const hasAge = new Boolean(age);

// good
const hasAge = Boolean(age);

// best
const hasAge = !!age;

命名規則

避免使用一個字母命名

eslint

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

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

使用小駝峯式命名對象、函數、實例

eslint

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

// good
const thisIsMyObject = {};
function thisIsMyFunction() {}

使用大駝峯式命名類

eslint

// 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',
});

不要使用前置或後置下劃線(除非引入的第三方庫自己使用)

eslint

JavaScript 沒有私有屬性或私有方法的概念。儘管前置下劃線一般的概念上意味着「private」,事實上,這些屬性是徹底公有的,所以這部分也是你的API的內容。這一律念可能會致使開發者誤覺得更改這個不會致使崩潰或者不須要測試。 若是你想要什麼東西變成「private」,那就不要讓它在這裏出現。
// bad
this.__firstName__ = 'Panda';
this.firstName_ = 'Panda';
this._firstName = 'Panda';

// good
this.firstName = 'Panda';

不要保存引用 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);
  };
}

保證文件名、export 模塊名以及 import 模塊名一致

// 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

export default 一個函數時、函數名小駝峯式,文件與函數名一致

function makeStyleGuide() {
  // ...
}

export default makeStyleGuide;

export 一個結構體、類、單例、函數庫或者對象時,使用大駝峯式

const Helpers = {
  guid: () => return uuid(),
};

export default Helpers;

簡稱或縮寫應該所有大寫或者所有小寫

名字是給人讀的,不是爲了適應電腦的算法
// bad
import SmsContainer from './containers/SmsContainer';

// bad
const HttpRequests = [
  // ...
];

// good
import SMSContainer from './containers/SMSContainer';

// good
const HTTPRequests = [
  // ...
];

// best
import TextMessageContainer from './containers/TextMessageContainer';

// best
const Requests = [
  // ...
];

使用全大寫字母設置靜態變量

  1. 導出變量
  2. const 定義的, 保證不能被改變
  3. 這個變量是可信的,他的子屬性都是不能被改變的
通常咱們都將項目的全局參數使用這種 全大寫+下劃線分隔的常量 來定義一些系統配置參數導出,好比 const LIST_VIEW_PAGE_SIZE = 10 能夠表示列表頁每次加載10條數據;

若是導出項目是一個對象,那麼必須保證這個對象的全部屬性都是不可變的,同時,它的屬性再也不是全大寫,而是使用小寫駝峯式。

// 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'
};

訪問器

若非必要,不要使用訪問器

因爲 JavaScript 的 getters/setters 是有反作用的,並且會讓他人在查看代碼的時候難以理解,後期也會難以維護,因此不推薦使用訪問器函數,若是非要使用,可使用本身實現的 getVal()setVal()

// bad
class Dragon {
  get age() {
    // ...
  }

  set age(value) {
    // ...
  }
}

// good
class Dragon {
  getAge() {
    // ...
  }

  setAge(value) {
    // ...
  }
}

若是屬性或者方法是一個布爾判斷值,那麼使用 isVal() 或者 hasVal()

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

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

若是非要使用 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];
  }
}

事件

當你須要向事件附加數據時,將數據封裝成爲一個對象,而不是使用原始值,這會使得之後能夠很方便的增長附加值的字段。

// bad
$(this).trigger('listingUpdated', listing.id);

// ...

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

而是:

// good
$(this).trigger('listingUpdated', { listingID: listing.id });

// ...

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

jQuery

爲全部 jQuery 對象加上 $ 前綴

// bad
const sidebar = $('.sidebar');

// good
const $sidebar = $('.sidebar');

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

緩存 jQuery 結果

// bad
function setSidebar() {
  $('.sidebar').hide();

  // ...

  $('.sidebar').css({
    'background-color': 'pink',
  });
}

// good
function setSidebar() {
  const $sidebar = $('.sidebar');
  $sidebar.hide();

  // ...

  $sidebar.css({
    'background-color': 'pink',
  });
}

使用級聯查詢 $('.sidebar ul') 或者子父級查詢 $('.sidebar > ul')

在 jQuery 對象查詢做用域下使用 find 方法

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

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

// good
$('.sidebar ul').hide();

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

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

ES5 兼容性

直接參考 Kangax 提供的 ES5 兼容性列表

相關文章
相關標籤/搜索