摘要:論語中各篇一般都是以第一章的前二三個字作為該篇的篇名。不允許使用命名參數(shù)的優(yōu)先級高于高于每個函數(shù)作用域自帶的對象,這會導(dǎo)致函數(shù)自帶的值被覆蓋。
類型 基本類型
你可以直接獲取到基本類型的值
string
number
boolean
null
undefined
symbol
const foo = 1; let bar = foo; bar = 9; console.log(foo, bar); // => 1, 9
注意:Symbols 不能被完整的 polyfill,所以,在不支持 Symbols 的環(huán)境下中,不應(yīng)該使用 symbol 類型。復(fù)雜類型
復(fù)雜類型賦值就是獲取到他的引用的值,相當(dāng)于引用傳遞
object
array
function
const foo = [1, 2]; const bar = foo; bar[0] = 9; console.log(foo[0], bar[0]); // => 9, 9參考 永遠(yuǎn)都使用 const
為了確保你不會改變你的初始值,重復(fù)引用會導(dǎo)致一些不可預(yù)見的 bug,還會讓代碼難以理解,所有的賦值都應(yīng)該使用 const,避免使用 var。
eslint
prefer-const
no-const-assign
// bad var a = 1; var b = 2; // good const a = 1; const b = 2;可以使用 let
如果你一定要對參數(shù)重新賦值,那就使用 let,而不是 var, let 是塊級作用域,而 ver 是函數(shù)級作用域。
eslint
no-var
// bad var count = 1; if (true) { count += 1; } // good let count = 1; if (true) { count += 1; }注意 const 與 let 的塊級作用域
const 與 let 聲明的常量與變量都只存在于定義它們的那個塊級作用域中。
{ let a = 1; const b = 1; } console.log(a); // ReferenceError console.log(b); // ReferenceError對象 永遠(yuǎn)使用字面量創(chuàng)建對象
eslint
no-new-object
// bad const obj = new Object(); // good const obj = {};使用計算屬性名
當(dāng)你需要創(chuàng)建一個帶有 動態(tài)屬性名 的對象時,請將所有的屬性定義放在一起,可以使用 計算屬性名。
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 };對象方法簡寫
eslint
object-shorthand
// bad const atom = { value: 1, add: function (value) { return atom.value + value; } }; // good const atom = { value: 1, add(value) { return atom.value + value; } };屬性值縮寫
eslint
object-shorthand
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
quote-props
// bad const bad = { "foo": 1, "bar": 2, "foo-bar": 3, }; // good const good = { foo: 1, bar: 2, "foo-bar": 3, };不直接調(diào)用對象原型上的方法
不直接調(diào)用一個對象的 hasOwnProperty、propertyIsEnumerable、isPrototypeOf 等這些原型的方法,在某些情況下,這些方法可能會被屏蔽掉,比如 { 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);積極使用擴(kuò)展及解構(gòu)運(yùn)算 ...
在對象的 淺拷貝 時,更推薦使用擴(kuò)展運(yùn)算 { ...obj },而不是 Object.assign。
在獲取對象指定的幾個屬性時,使用解構(gòu)運(yùn)算 { foo, bar, ...rest } = obj
eslint
prefer-destructuring
// very bad const original = { a: 1, b: 2 }; const copied = Object.assign(original, { c: 3 }); // 這將導(dǎo)致 original 也被修改 delete copied.a; // 這樣操作之后會導(dǎo)致 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 }; // 解構(gòu)運(yùn)算與 `rest` 賦值運(yùn)算 const obj = { a: 1, b: 2, c: 3 }; const { a, b } = obj; // 從對象 obj 中解構(gòu)出 a, b 兩個屬性的值,并賦值給名為 a,b 的常量 const { a, ...rest } = obj; // 從對象 obj 中解構(gòu)出 a 的值,并賦值給名為 a 的常量,同時,創(chuàng)建一個由所有其它屬性組成的名為 `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}`;返回多值時,使用對象解構(gòu),而非數(shù)組結(jié)構(gòu)
由于 JavaScript 不支持多值返回,當(dāng)一個函數(shù)或者方法有多個值需要創(chuàng)建時,請為每一個值命名,并以所有值組成的對象為單一值返回,而不是以數(shù)組的形式返回。
// bad function processInput(input) { return [left, right, top, bottom]; } const [left, _, top] = processInput(input); // 調(diào)用者需要在調(diào)用時,明確的知道每一個索引上的值是什么 ,且無法跳越前面的值取后面的值 // good function processInput(input) { return { left, right, top, bottom }; } const { left, top } = processInput(input); // 調(diào)用者可以明確的指定需要哪個值,而且不需要創(chuàng)建多余的變量數(shù)組 使用字面量賦值
eslint
no-array-constructor
// bad const items = new Array(); // good const items = [];使用 .push 方法代替直接索引賦值
const items = []; // bad items[items.length] = "new item"; // good items.push("new item");使用擴(kuò)展運(yùn)算符進(jìn)行淺拷貝
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 ];使用 ... 運(yùn)算符代替 Array.from
當(dāng)需要將一個可迭代的對象轉(zhuǎn)換成數(shù)組時,推薦使用 ... 操作符。
const elements = document.querySelectorAll(".foobar"); // not bad const nodes = Array.from(elements); // good const nodes = [ ...elements ];使用 ... 解構(gòu)數(shù)組
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 將類數(shù)組對象轉(zhuǎn)成數(shù)組
參考: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 對類數(shù)組對象進(jìn)行遍歷
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);在數(shù)組方法的回調(diào)函數(shù)中,永遠(yuǎn)返回正確的值
// bad - 當(dāng)?shù)谝淮蔚瓿芍螅?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));一個數(shù)組有多行時,在 [ 與 ] 處斷行
// 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 永遠(yuǎn)使用單引號 "":
eslint
quotes
// bad const name = "Parc M.G"; const name = `Parc M.G`; // good const name = "Parc M.G";超長的字符串,不應(yīng)該使用多行串聯(lián)
// bad const content = "《學(xué)而》是《論語》第一篇的篇名。《論語》中各篇一般都是以第 一章的前二三個字作為該篇的篇名。《學(xué)而》一篇包括16章,內(nèi)容涉及諸多方面。其中重 點是「吾日三省吾身」;「節(jié)用而愛人,使民以時」;「禮之用,和為貴」以及仁、孝、 信等道德范疇。"; const content = "《學(xué)而》是《論語》第一篇的篇名。《論語》中各篇一般都是以第" + "一章的前二三個字作為該篇的篇名。《學(xué)而》一篇包括16章,內(nèi)容涉及諸多方面。其中重" + "點是「吾日三省吾身」;「節(jié)用而愛人,使民以時」;「禮之用,和為貴」以及仁、孝、" + "信等道德范疇。"; // good const content = "《學(xué)而》是《論語》第一篇的篇名。《論語》中各篇一般都是以第一章的前二三個字作為該篇的篇名。《學(xué)而》一篇包括16章,內(nèi)容涉及諸多方面。其中重點是「吾日三省吾身」;「節(jié)用而愛人,使民以時」;「禮之用,和為貴」以及仁、孝、信等道德范疇。";使用模板而非拼接來組織可編程字符串
eslint
prefer-template
template-curly-spacing
// bad function hello(name) { return "你好," + name + "!"; } function hello(name) { return ["你好,", name, "!"].join(""); } function hello(name) { return `你好,${ name }!`; } // good function hello(name) { return `你好,${name}!`; }永遠(yuǎn)不使用 eval()
eslint
no-eval
若非必要,不使用轉(zhuǎn)義字符eslint
no-useless-escape
// bad const foo = ""this" is "quoted""; // good const foo = " his" is "quoted""; // best const foo = `"this" is "quoted"`;函數(shù) 使用命名函數(shù)表達(dá)式,而不是函數(shù)聲明
eslint
func-styl
使用函數(shù)聲明,它的作用域會被提前,這意味著很容易在一個文件里面引用一個還未被定義的函數(shù),這樣大大傷害了代碼的可讀性和可維護(hù)性,若一個函數(shù)很大很復(fù)雜,那么應(yīng)該考慮將該函數(shù)多帶帶提取到一個文件中,抽離成一個模塊,同時不要忘記給表達(dá)式顯示的命名,這消除了由匿名函數(shù)在錯誤調(diào)用棧中產(chǎn)生的所有假設(shè)。
// bad function foo() { // ... } // bad const foo = function () { // ... } // good const foo = function foo() { // ... } // best const foo = function longUniqueMoreDescriptiveLexicalFoo() { // ... }把立即執(zhí)行函數(shù)包裹在圓括號里
eslint
wrap-iife
(function () { console.log("Welcome to the ParcMG world."); }());不要在非函數(shù)塊內(nèi)聲明函數(shù)
雖然運(yùn)行環(huán)境允許你這樣做,但是不同環(huán)境的解析方式不一樣。
eslint
no-loop-func
//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(); }
不允許使用 arguments 命名參數(shù)注意:在 ECMA-262 中,塊(block) 的定義是:一系列語句,但函數(shù)聲明不是一個語句,命名函數(shù)表達(dá)式是一個語句。
// bad if (currentUser) { function test() { console.log("Nope."); } } // good let test; if (currentUser) { test = () => { console.log("Yup."); }; }
arguments 的優(yōu)先級高于高于每個函數(shù)作用域自帶的 arguments 對象,這會導(dǎo)致函數(shù)自帶的 arguments 值被覆蓋。
// bad function foo(name, options, arguments) { // ... } // good function foo(name, options, args) { // ... }不要在函數(shù)體內(nèi)使用 arguments,使用 ...rest 代替
eslint
prefer-rest-params
... 明確出你想用那個參數(shù),同時,rest 是一個真數(shù)組,而不是一個類數(shù)組的 arguments
// bad function concatenateAll() { const args = Array.prototype.slice.call(arguments); return args.join(""); } // good function concatenateAll(...args) { return args.join(""); }使用默認(rèn)參數(shù),而不是在函數(shù)體內(nèi)對參數(shù)重新賦值
// really bad function handleThings(options) { options = options || {}; } // still bad function handleTings(options) { if (options === void 0) { options = {}; } } // good function handleThings(options = {}) { }默認(rèn)參數(shù)要避免副作用
// 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); }把默認(rèn)參數(shù)放在最后
// bad function handleTings(options = {}, name) { // ... } // good function handleTings(name, options = {}) { // ... }不要使用函數(shù)構(gòu)造器構(gòu)造函數(shù)
eslint
no-new-func
// 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;函數(shù)簽名部分要有空格
eslint
space-before-function-paren
space-before-blocks
// bad const f = function(){}; const g = function (){}; const h = function() {}; // good const f = function a() {};不修改參數(shù)
eslint
no-param-reassign
函數(shù)簽名時定義的參數(shù),在函數(shù)體內(nèi)不允許被重新賦值(包含參數(shù)本身,若參數(shù)為對象,還包括該對象所有屬性的值),
一個函數(shù)應(yīng)該是沒有任何副作用的。
// 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 操作符 ... 調(diào)用多變參數(shù)函數(shù)
eslint
prefer-spread
// 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]);若函數(shù)簽名包含多個參數(shù)需要使用多行,那就每行有且僅有一個參數(shù)
// bad function foo(bar, baz, quux) { // ... } // good 縮進(jìn)不要太過分 function foo( bar, baz, quux, ) { // ... } // bad console.log(foo, bar, baz); // good console.log( foo, bar, baz, );箭頭函數(shù) 當(dāng)你一定要用函數(shù)表達(dá)式的時候,就使用箭頭函數(shù)
eslint
prefer-array-callback
arrow-spacing
// 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; });如果函數(shù)體有且僅有一個沒有副作用的表達(dá)式,那么刪除大括號和 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 })); // 表達(dá)式有副作用就不要用隱式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; });若表達(dá)式包含多行,用圓括號包裹起來
// bad ["get", "post", "put"].map(httpMethod => Object.prototype.hasOwnProperty.call( httpMagicObjectWithAVeryLongName, httpMethod ) ); // good ["get", "post", "put"].map(httpMethod => ( Object.prototype.hasOwnProperty.call( httpMagicObjectWithAVeryLongName, httpMethod ) ));若函數(shù)只有一個參數(shù),且沒有大括號,那就刪除圓括號,否則,參數(shù)總是放在圓括號里。
eslint
arrow-parens
// 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; });避免箭頭函數(shù)(=>)和比較操作符(<=, >=)混淆.
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; };在隱式return中強(qiáng)制約束函數(shù)體的位置, 就寫在箭頭后面
eslint
implicit-arrow-linebreak
// bad (foo) => bar; (foo) => (bar); // good (foo) => bar; (foo) => (bar); (foo) => ( bar )類與構(gòu)造器 使用構(gòu)造器,而不是 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 實現(xiàn)繼承
它是一種內(nèi)置的方法來繼承原型功能而不打破 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 實現(xiàn)方法鏈
// 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()}`; } }不要寫無用的構(gòu)造函數(shù)
eslint
no-useless-constructor
// bad class Jedi { constructor() {} getName() { return this.name; } } // bad class Rey extends Jedi { // 這種構(gòu)造函數(shù)是不需要寫的 constructor(...args) { super(...args); } } // good class Rey extends Jedi { constructor(...args) { super(...args); this.name = "Rey"; } }避免重復(fù)類成員
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; } }模塊 使用 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";不要直接從 import 中 export
雖然一行是簡潔的,有一個明確的方式進(jìn)口和一個明確的出口方式來保證一致性。
// bad export { es6 as default } from "./Component"; // good import { es6 } from "./Component"; export default es6;一個路徑只 import 一次
eslint
no-duplicate-imports
從同一個路徑下import多行會使代碼難以維護(hù)
// 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
import/no-mutable-exports
變化通常都是需要避免,特別是當(dāng)你要輸出可變的綁定。雖然在某些場景下可能需要這種技術(shù),但總的來說應(yīng)該導(dǎo)出常量。
// bad let foo = 3; export { foo } // good const foo = 3; export { foo }在一個單一導(dǎo)出模塊里,使用 export default
eslint
import/prefer-default-export
鼓勵使用更多文件,每個文件只做一件事情并導(dǎo)出,這樣可讀性和可維護(hù)性更好。
// bad export function foo() {} // good export default function foo() {}import 應(yīng)該放在所有其它語句之前
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();多行import應(yīng)該縮進(jìn),就像多行數(shù)組和對象字面量
花括號與樣式指南中每個其他花括號塊遵循相同的縮進(jìn)規(guī)則,逗號也是。
// bad import {longNameA, longNameB, longNameC, longNameD, longNameE} from "path"; // good import { longNameA, longNameB, longNameC, longNameD, longNameE, } from "path";若使用 webpack,不允許在 import 中使用 webpack loader 語法
eslint
import/no-webpack-loader-syntax
一旦用 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
no-iterator
no-restricted-syntax
用JavaScript高級函數(shù)代替 for-in、for-of
這強(qiáng)調(diào)了我們不可變的規(guī)則。 處理返回值的純函數(shù)比副作用更容易。
用數(shù)組的這些迭代方法: map()、every()、filter()、find()、findIndex()、reduce()、some()……
用對象的這些方法 Object.keys()、Object.values()、Object.entries 去產(chǎn)生一個數(shù)組, 這樣你就能去遍歷對象了。
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
generator-star-spacing
它在es5上支持的不好
如果一定要用,那么一定需要注意一點:function 與 * 是同一概念關(guān)鍵字, * 并不是 function 的修飾符, function* 是一個與 function 完全不一樣的獨(dú)特結(jié)構(gòu)。
// 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
dot-notation
這條,涉及一個曾經(jīng)阿里出過一個看似簡單,實則很難的面試題,你就算猜對一個,你也不一定能說出原理:
a.b.c.d和a"b"["d"],哪個性能更高
到這里,突然想起這個梗,有興趣的可以翻看一下 這里。
const luke = { jedi: true, age: 28, }; // bad const isJedi = luke["jedi"]; // good const isJedi = luke.jedi;當(dāng)獲取屬性名稱本身是一個變量是,使用 [] 訪問
const luke = { jedi: true, age: 28, }; function getProp(prop) { return luke[prop]; } const isJedi = getProp("jedi");冪等使用 ** 操作符
eslint
no-restricted-properties
// bad const binary = Math.pow(2, 10); // good const binary = 2 ** 10;變量 永遠(yuǎn)使用 const 或者 let,不使用 var
eslint
no-undef
prefer-const
// bad superPower = new SuperPower(); // good const superPower = new SuperPower();每一個變量都用一個 const 或者 let
eslint
one-var
扯蛋的理由:這種方式很容易去聲明新的變量,你不用去考慮把;調(diào)換成,,或者引入一個只有標(biāo)點的不同的變化。真正的理由:做法也可以是你在調(diào)試的時候單步每個聲明語句,而不是一下跳過所有聲明。
// 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
no-multi-assign
鏈接變量分配會隱匿創(chuàng)建全局變量
// 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` 也是如此不使用一元自增自減運(yùn)算(++、--)
eslint
no-plusplus
根據(jù) eslint 文檔,一元增量和減量語句受到自動分號插入的影響,并且可能會導(dǎo)致應(yīng)用程序中的值遞增或遞減的無聲錯誤。 使用 num + = 1 而不是 num ++ 或 num ++ 語句來表達(dá)你的值也是更有表現(xiàn)力的。 禁止一元增量和減量語句還會阻止您無意地預(yù)增/預(yù)減值,這也會導(dǎo)致程序出現(xiàn)意外行為。
// 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
operator-linebreak
如果賦值語句超出了 max-len 配置,那么給值前面加上括號。
// bad const foo = superLongLongLongLongLongLongLongLongFunctionName(); // bad const foo = "superLongLongLongLongLongLongLongLongString"; // good const foo = ( superLongLongLongLongLongLongLongLongFunctionName() ); // good const foo = "superLongLongLongLongLongLongLongLongString";不允許聲明不使用的變量
eslint
no-unused-vars
// bad var some_unused_var = 42; // 寫了沒用 var y = 10; y = 5; // 變量改了自己的值,也沒有用這個變量 var z = 0; z = z + 1; // 參數(shù)定義了但未使用 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" 現(xiàn)在就是一個沒有 "type" 屬性的 "data" 對象變量提升 永遠(yuǎn)不要使用 var
var 聲明會將變量聲明提升到作用域的最前面,但是他的值卻只有在運(yùn)行到代碼行時才會被賦值,永遠(yuǎn)都使用 const 與 let,了解 時效區(qū)(Temporal Dead Zones) 的相關(guān)知識,也還要知道為什么 typeof 不再安全。
// 我們知道這個不會工作,假設(shè)沒有定義全局的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; }匿名函數(shù)表達(dá)式與 var 的情況一樣
function example() { console.log(anonymous); // => undefined anonymous(); // => TypeError anonymous is not a function var anonymous = function () { console.log("anonymous function expression"); }; }已命名函數(shù)表達(dá)式只提升變量名,而不是函數(shù)名或者函數(shù)體
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"); }; } // 函數(shù)名和變量名一樣是也如此 function example() { console.log(named); // => undefined named(); // => TypeError named is not a function var named = function named() { console.log("named"); }; }函數(shù)聲明則提升了函數(shù)名和函數(shù)體
function example() { superPower(); // => Flying function superPower() { console.log("Flying"); } }比較操作符 永遠(yuǎn)使用 === 與 !==,而不是 == 與 !=
eslint
eqeqeq
if 條件語句的強(qiáng)制 toBooleanif 條件語句的強(qiáng)制 toBoolean 總是遵循以下規(guī)則:
Objects 總是計算成 true
Undefined 總是計算 成 false
Null 總是計算成 false
Booleans 計算成它本身的布爾值
Numbers
+0、-0 或者 NaN 總是計算成 false
其它的全部為 true
Strings
"" 計算成 false
其它全部為 true
注意:NaN 是不等于 NaN 的,請使用 isNaN() 檢測。
if ([0] && []) { // true // 數(shù)組(即使是空數(shù)組)是對象,對象會計算成true } console.log(NaN === NaN) // => false console.log(isNaN(NaN)) // => true布爾值要使用縮寫,但是字符串與數(shù)字要明確比較對象
// bad if (isValid === true) { // ... } // good if (isValid) { // ... } // bad if (name) { // ... } // good if (name !== "") { // ... } // bad if (collection.length) { // ... } // good if (collection.length > 0) { // ... }在 switch 的 case 與 default 分句中使用大括號創(chuàng)建語法聲明區(qū)域
eslint
no-case-declarations
語法聲明在整個 switch 的代碼塊里都可見,但是只有當(dāng)其被分配后才會初始化,他的初始化時當(dāng)這個 case 被執(zhí)行時才產(chǎn)生。 當(dāng)多個 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 {} } }三元運(yùn)算符不能被嵌套
eslint
no-nested-ternary
// 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;避免不必要的三元表達(dá)式
// 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;除非優(yōu)先級顯而易見,否則使用圓括號來混合操作符
eslint
no-mixed-operators
開發(fā)者需要以最顯而易見的方式明確自己的意圖與邏輯
// 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;區(qū)塊 用大括號包裹多行代碼
eslint
nonblock-statement-body-position
// 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 以及 else 與 if 的關(guān)閉大括號在同一行
eslint
brace-style
// bad if (test) { thing1(); thing2(); } else { thing3(); } // good if (test) { thing1(); thing2(); } else { thing3(); }if 語句中的 return
eslint
no-else-return
如果 if 語句中總是需要用 return 返回,那么后續(xù)的 else 就不需要寫了
如果 if 塊中包含 return,它后面的 else if 也包含了 return,那就應(yīng)該把 else if 的 return 分到多個 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; } }控制語句 當(dāng)你的控制語句 (if、while)等太長,或者超過最大長度限制時,把每一個判斷條件都放到多帶帶一行去,邏輯操作符放在行首
// 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} 標(biāo)簽名 // @return {Element} 新元素 function make(tag) { // ... return element; } // good /** * make() 基于傳入的 `tag` 名返回一個新元素 * @param {String} 標(biāo)簽名 * @param {Element} 新元素 */ function make(tag) { // ... return element; }單行注釋用 //
將單行注釋放在被注釋區(qū)域上面。如果注釋不是在第一行,那么注釋前面就空一行
// bad const active = true; // is current tab // good // 當(dāng)前激活狀態(tài)的 tab const active = true; // bad function getType() { console.log("fetching type..."); // 設(shè)置默認(rèn) `type` 為 "no type" const type = this._type || "no type"; return type; } // good function getType() { console.log("fetching type..."); // 設(shè)置默認(rèn) `type` 為 "no type" const type = this._type || "no type"; return type; } // also good function getType() { // 設(shè)置默認(rèn) `type` 為 "no type" const type = this._type || "no type"; return type; }所有注釋開頭空一個,方便閱讀
eslint
space-comment
// bad //當(dāng)前激活的 tab const active = true; // good // 當(dāng)前激活的 tab const active = true; // bad /** *make() 基于傳入的 `tag` 名返回一個新元素 *@param {String} 標(biāo)簽名 *@param {Element} 新元素 */ function make(tag) { // ... return element; } // good /** * make() 基于傳入的 `tag` 名返回一個新元素 * @param {String} 標(biāo)簽名 * @param {Element} 新元素 */ function make(tag) { // ... return element; }積極使用 FIXME 與 TODO
當(dāng)你的注釋需要向注釋閱讀者或者代碼的后繼開發(fā)者明確的表述一種期望時,應(yīng)該積極使用 FIXME 以及 TODO 前綴,這有助于其他的開發(fā)理解你指出的需要重新訪問的問題,也方便自己日后有時間的時候再次回顧當(dāng)時沒有解決或者計劃去做而沒有做的事情。
FIXME:這里有一個問題,現(xiàn)在還沒有影響大局,但是更希望解決這個問題或者找到一個更優(yōu)雅的方式去實現(xiàn)
TODO:計劃在這里去實現(xiàn)某些功能,現(xiàn)在還沒有實現(xiàn)
// 使用 FIXME: class Calculator extends Abacus { constructor() { super(); // FIXME: 不應(yīng)該在此處使用全局變量 total = 0; } } // 使用 TODO: class Calculator extends Abacus { constructor() { super(); // TODO: total 應(yīng)該應(yīng)該從一個參數(shù)中獲取并初始化 this.total = 0; } }空格 代碼縮進(jìn)總是使用兩個空格
eslint
indent
// bad function foo() { ????const name; } // bad function bar() { ?const name; } // good function baz() { ??const name; }在大括號前空一格
eslint
space-before-blocks
// 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", });關(guān)鍵字空格
eslint
keyword-spacing
在控制語句( if, while 等)的圓括號前空一格。在函數(shù)調(diào)用和定義時,參數(shù)列表和函數(shù)名之間不空格。
// bad if(isJedi) { fight (); } // good if (isJedi) { fight(); } // bad function fight () { console.log ("Swooosh!"); } // good function fight() { console.log("Swooosh!"); }用空格來隔開運(yùn)算符
eslint
space-infix-ops
// bad const x=y+5; // good const x = y + 5;文件結(jié)尾加一個換行
eslint
eol-last
// bad function doSmth() { var foo = 2; }
// good function doSmth() { var foo = 2; }使用多行縮進(jìn)的方式進(jìn)行一個長方法鏈調(diào)用
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(); // 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
padded-blocks
// 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
space-in-parens
// 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
array-bracket-spacing
// bad const foo = [ 1, 2, 3 ]; console.log(foo[ 0 ]); // good, 逗號分隔符還是要空格的 const foo = [1, 2, 3]; console.log(foo[0]);花括號里要加空格
eslint
object-curly-spacing
// bad const foo = {clark: "kent"}; // good const foo = { clark: "kent" };避免一行代碼超過100個字符(包含空格)
eslint
max-len
為了確保代碼的人類可讀性與可維護(hù)性,代碼行應(yīng)避免超過一定的長度
// 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."));作為語句的花括號里不應(yīng)該加空格
eslint
block-spacing
// bad function foo() {return true;} if (foo) { bar = 0;} // good function foo() { return true; } if (foo) { bar = 0; }, 前不要空格,,后需要空格
eslint
comma-spacing
// bad var foo = 1,bar = 2; var arr = [1 , 2]; // good var foo = 1, bar = 2; var arr = [1, 2];計算屬性內(nèi)要空格
eslint
computed-property-spacing
// bad obj[foo ] obj[ "foo"] var x = {[ b ]: a} obj[foo[ bar ]] // good obj[foo] obj["foo"] var x = { [b]: a } obj[foo[bar]]調(diào)用函數(shù)時,函數(shù)名和小括號之間不要空格
eslint
func-call-spacing
// bad func (); func (); // good func();在對象的字面量屬性中, key 與 value 之間要有空格
eslint
key-spacing
// bad var obj = { "foo" : 42 }; var obj2 = { "foo":42 }; // good var obj = { "foo": 42 };行末不要空格
eslint
no-trailing-spaces
避免出現(xiàn)連續(xù)多個空行,文件末尾只允許空一行eslint
no-multiple-empty-lines
// bad var x = 1; var y = 2; // good var x = 1; var y = 2;逗號 不要前置逗號
eslint
comma-style
// 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", };額外結(jié)尾逗號
eslint
comma-dangle
就算項目有可能運(yùn)行在舊版本的瀏覽器中,但是像 Babel 這樣的轉(zhuǎn)換器都會在轉(zhuǎn)換代碼的過程中刪除這些多余逗號,所以,大膽使用它,完全不會有副作用產(chǎn)生,相反的,他能讓我們更方便的給對象或者多行數(shù)組增加、刪除屬性或者元素,同時,還能讓我們的 git diffs 更清潔。
// bad - 沒有結(jié)尾逗號的 git diff const hero = { firstName: "Florence", - lastName: "Nightingale" + lastName: "Nightingale", + inventorOf: ["coxcomb chart", "modern nursing"] }; // good - 有結(jié)尾逗號的 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" 元素后面,絕對不能出現(xiàn)逗號) function createHero( firstName, lastName, inventorOf, ...heroArgs ) { // does nothing } // bad createHero( firstName, lastName, inventorOf ); // good createHero( firstName, lastName, inventorOf, ); // good (在一個 "rest" 元素后面,絕對不能出現(xiàn)逗號) createHero( firstName, lastName, inventorOf, ...heroArgs )分號 永遠(yuǎn)明確的使用分號結(jié)束你的代碼行
eslint
semi
當(dāng) JavaScript 遇到?jīng)]有分號結(jié)尾的一行,它會執(zhí)行 自動插入分號 Automatic Semicolon Insertion 這一規(guī)則來決定行末是否加分號。如果JavaScript在你的斷行里錯誤的插入了分號,就會出現(xiàn)一些古怪的行為。當(dāng)新的功能加到JavaScript里后, 這些規(guī)則會變得更復(fù)雜難懂。顯示的結(jié)束語句,并通過配置代碼檢查去捕獲沒有帶分號的地方可以幫助你防止這種錯誤。
// bad (function () { const name = "Skywalker" return name })() // good (function () { const name = "Skywalker"; return name; }()); // good, 行首加分號,避免文件被連接到一起時立即執(zhí)行函數(shù)被當(dāng)做變量來執(zhí)行。 ;(() => { const name = "Skywalker"; return name; }());強(qiáng)類型轉(zhuǎn)換 在語句開始執(zhí)行強(qiáng)制類型轉(zhuǎn)換 使用 String 進(jìn)行字符類型轉(zhuǎn)換
eslint
no-new-wrappers
// => this.reviewScore = 9; // bad const totalScore = new String(this.reviewScore); // typeof totalScore is "object" not "string" // bad const totalScore = this.reviewScore + ""; // invokes this.reviewScore.valueOf() // bad const totalScore = this.reviewScore.toString(); // 不保證返回string // good const totalScore = String(this.reviewScore);使用 Number 進(jìn)行數(shù)字類型轉(zhuǎn)換
eslint
radix
使用 parseInt 轉(zhuǎn)換 string 通常都需要帶上基數(shù)。
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);在注釋中說明為什么要使用移位運(yùn)算
如果你感覺 parseInt 滿足不要你的需求,想使用移位進(jìn)行運(yùn)算,那么你一定要寫明白,這是因為 性能問題,同時,你還需要注意,數(shù)字使用 64 位 表示的,但移位運(yùn)算常常返回的是 32 位的整形,移位運(yùn)算對于大于 32 位的整數(shù)會導(dǎo)致一些 意外行為,最大的32位整數(shù)是 2,147,483,647。
// good /** * parseInt是代碼運(yùn)行慢的原因 * 用Bitshifting將字符串轉(zhuǎn)成數(shù)字使代碼運(yùn)行效率大幅增長 */ 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;命名規(guī)則 避免使用一個字母命名
eslint
id-length
// bad function q() { // ... } // good function query() { // ... }使用小駝峰式命名對象、函數(shù)、實例
eslint
camelcase
// bad const OBJEcttsssss = {}; const this_is_my_object = {}; function c() {} // good const thisIsMyObject = {}; function thisIsMyFunction() {}使用大駝峰式命名類
eslint
new-cap
// bad function user(options) { this.name = options.name; } const bad = new user({ name: "nope", }); // good class User { constructor(options) { this.name = options.name; } } const good = new User({ name: "yup", });不要使用前置或后置下劃線(除非引入的第三方庫本身使用)
eslint
no-underscore-dangle
JavaScript 沒有私有屬性或私有方法的概念。盡管前置下劃線通常的概念上意味著“private”,事實上,這些屬性是完全公有的,因此這部分也是你的API的內(nèi)容。這一概念可能會導(dǎo)致開發(fā)者誤以為更改這個不會導(dǎo)致崩潰或者不需要測試。 如果你想要什么東西變成“private”,那就不要讓它在這里出現(xiàn)。
// bad this.__firstName__ = "Panda"; this.firstName_ = "Panda"; this._firstName = "Panda"; // good this.firstName = "Panda";不要保存引用 this
用箭頭函數(shù)或函數(shù)綁定——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.jsexport default 一個函數(shù)時、函數(shù)名小駝峰式,文件與函數(shù)名一致
function makeStyleGuide() { // ... } export default makeStyleGuide;當(dāng) export 一個結(jié)構(gòu)體、類、單例、函數(shù)庫或者對象時,使用大駝峰式
const Helpers = { guid: () => return uuid(), }; export default Helpers;簡稱或縮寫應(yīng)該全部大寫或者全部小寫
名字是給人讀的,不是為了適應(yīng)電腦的算法
// 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 = [ // ... ];使用全大寫字母設(shè)置靜態(tài)變量
導(dǎo)出變量
是 const 定義的, 保證不能被改變
這個變量是可信的,他的子屬性都是不能被改變的
一般我們都將項目的全局參數(shù)使用這種 全大寫+下劃線分隔的常量 來定義一些系統(tǒng)配置參數(shù)導(dǎo)出,比如 const LIST_VIEW_PAGE_SIZE = 10 可以表示列表頁每次加載10條數(shù)據(jù);如果導(dǎo)出項目是一個對象,那么必須保證這個對象的所有屬性都是不可變的,同時,它的屬性不再是全大寫,而是使用小寫駝峰式。
// 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 是有副作用的,而且會讓他人在查看代碼的時候難以理解,后期也會難以維護(hù),所以不推薦使用訪問器函數(shù),如果非要使用,可以使用自己實現(xiàn)的 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]; } }事件
當(dāng)你需要向事件附加數(shù)據(jù)時,將數(shù)據(jù)封裝成為一個對象,而不是使用原始值,這會使得以后可以很方便的增加附加值的字段。
// 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 結(jié)果
// bad function setSidebar() { $(".sidebar").hide(); // ... $(".sidebar").css({ "background-color": "pink", }); } // good function setSidebar() { const $sidebar = $(".sidebar"); $sidebar.hide(); // ... $sidebar.css({ "background-color": "pink", }); }使用級聯(lián)查詢 $(".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 兼容性列表
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://specialneedsforspecialkids.com/yun/109711.html
摘要:這樣的變量增加了代碼量,并且混淆讀者。錯誤代碼示例變量雖然聲明了,但沒被使用持續(xù)更新 JavaScript 編碼規(guī)范 一、命名規(guī)范 1. 變量 命名方法:小駝峰式命名法(由小寫字母開始,后續(xù)每個單詞首字母都大寫) 命名建議:語義化的名詞 特殊:布爾值變量建議添加符合其含義的前綴動詞 is:是否 can:能不能 has:有沒有 示例: // 頁面標(biāo)題 let pageT...
摘要:當(dāng)然我們還可以引入框架,這些框架一般都自帶模板處理引擎,比如等語義化命名和語義化標(biāo)簽我們盡量多采用語義化來命名,并且采用語義化標(biāo)簽來書寫代碼,多用中新增的標(biāo)簽來書寫。 1.黃金法則(Golden rule) 不管有多少人參與同一個項目,一定要確保每一行代碼都像是同一個人編寫的。 Every line of code should appear to be written by a si...
摘要:編碼規(guī)范是獨(dú)角獸公司內(nèi)部的編碼規(guī)范,該項目是上很受歡迎的一個開源項目,在前端開發(fā)中使用廣泛,本文的配置規(guī)則就是以編碼規(guī)范和編碼規(guī)范作為基礎(chǔ)的。 更新時間:2019-01-22React.js create-react-app 項目 + VSCode 編輯器 + ESLint 代碼檢查工具 + Airbnb 編碼規(guī)范 前言 為什么要使用 ESLint 在項目開發(fā)過程中,編寫符合團(tuán)隊編碼規(guī)...
摘要:用兩個空格代替制表符這是唯一能保證在所有環(huán)境下獲得一致展現(xiàn)的方法。編輯器配置將你的編輯器按照下面的配置進(jìn)行設(shè)置,以免常見的代碼不一致和差異用兩個空格代替制表符保存文件時刪除尾部的空白符設(shè)置文件編碼為在文件結(jié)尾添加一個空白行。 黃金定律 永遠(yuǎn)遵循同一套編碼規(guī)范 - 可以是這里列出的,也可以是你自己總結(jié)的。如果發(fā)現(xiàn)規(guī)范中有任何錯誤,敬請指正。 HTML 語法 用兩個空格代替制表符 (ta...
摘要:寫在前面對于不同的編程語言來說,具體的編碼規(guī)范各不相同,但是其宗旨都是一致的,就是保證代碼在高質(zhì)量完成需求的同時具備良好的可讀性可維護(hù)性。減少標(biāo)簽的數(shù)量編寫代碼時,盡量避免多余的父元素。 寫在前面 對于不同的編程語言來說,具體的編碼規(guī)范各不相同,但是其宗旨都是一致的,就是保證代碼在高質(zhì)量完成需求的同時具備良好的可讀性、可維護(hù)性。 本文大部分內(nèi)容來自網(wǎng)上,僅供個人參考學(xué)習(xí)! 網(wǎng)絡(luò)上的知...
摘要:六字符編碼通過明確聲明字符編碼,能夠確保瀏覽器快速并容易的判斷頁面內(nèi)容的渲染方式。十一減少標(biāo)簽的數(shù)量編寫代碼時,盡量避免多余的父元素。未完待續(xù)編寫靈活穩(wěn)定高質(zhì)量的代碼的規(guī)范閱讀更多 一、唯一定律 無論有多少人共同參與同一項目,一定要確保每一行代碼都像是唯一個人編寫的。 二、HTML 2.1 語法 (1)用兩個空格來代替制表符(tab) -- 這是唯一能保證在所有環(huán)境下獲得一致展現(xiàn)的方法...
閱讀 1649·2021-11-16 11:44
閱讀 2393·2021-10-11 11:07
閱讀 4036·2021-10-09 09:41
閱讀 663·2021-09-22 15:52
閱讀 3187·2021-09-09 09:33
閱讀 2701·2019-08-30 15:55
閱讀 2284·2019-08-30 15:55
閱讀 837·2019-08-30 15:55