摘要:論語中各篇一般都是以第一章的前二三個字作為該篇的篇名。不允許使用命名參數的優先級高于高于每個函數作用域自帶的對象,這會導致函數自帶的值被覆蓋。
類型 基本類型
你可以直接獲取到基本類型的值
string
number
boolean
null
undefined
symbol
const foo = 1; let bar = foo; bar = 9; console.log(foo, bar); // => 1, 9
注意:Symbols 不能被完整的 polyfill,所以,在不支持 Symbols 的環境下中,不應該使用 symbol 類型。復雜類型
復雜類型賦值就是獲取到他的引用的值,相當于引用傳遞
object
array
function
const foo = [1, 2]; const bar = foo; bar[0] = 9; console.log(foo[0], bar[0]); // => 9, 9參考 永遠都使用 const
為了確保你不會改變你的初始值,重復引用會導致一些不可預見的 bug,還會讓代碼難以理解,所有的賦值都應該使用 const,避免使用 var。
eslint
prefer-const
no-const-assign
// bad var a = 1; var b = 2; // good const a = 1; const b = 2;可以使用 let
如果你一定要對參數重新賦值,那就使用 let,而不是 var, let 是塊級作用域,而 ver 是函數級作用域。
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對象 永遠使用字面量創建對象
eslint
no-new-object
// bad const obj = new Object(); // good const obj = {};使用計算屬性名
當你需要創建一個帶有 動態屬性名 的對象時,請將所有的屬性定義放在一起,可以使用 計算屬性名。
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, };不直接調用對象原型上的方法
不直接調用一個對象的 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);積極使用擴展及解構運算 ...
在對象的 淺拷貝 時,更推薦使用擴展運算 { ...obj },而不是 Object.assign。
在獲取對象指定的幾個屬性時,使用解構運算 { foo, bar, ...rest } = obj
eslint
prefer-destructuring
// 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
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");使用擴展運算符進行淺拷貝
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
quotes
// 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
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}!`; }永遠不使用 eval()
eslint
no-eval
若非必要,不使用轉義字符eslint
no-useless-escape
// bad const foo = ""this" is "quoted""; // good const foo = " his" is "quoted""; // best const foo = `"this" is "quoted"`;函數 使用命名函數表達式,而不是函數聲明
eslint
func-styl
使用函數聲明,它的作用域會被提前,這意味著很容易在一個文件里面引用一個還未被定義的函數,這樣大大傷害了代碼的可讀性和可維護性,若一個函數很大很復雜,那么應該考慮將該函數多帶帶提取到一個文件中,抽離成一個模塊,同時不要忘記給表達式顯示的命名,這消除了由匿名函數在錯誤調用棧中產生的所有假設。
// bad function foo() { // ... } // bad const foo = function () { // ... } // good const foo = function foo() { // ... } // best const foo = function longUniqueMoreDescriptiveLexicalFoo() { // ... }把立即執行函數包裹在圓括號里
eslint
wrap-iife
(function () { console.log("Welcome to the ParcMG world."); }());不要在非函數塊內聲明函數
雖然運行環境允許你這樣做,但是不同環境的解析方式不一樣。
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 命名參數注意:在 ECMA-262 中,塊(block) 的定義是:一系列語句,但函數聲明不是一個語句,命名函數表達式是一個語句。
// bad if (currentUser) { function test() { console.log("Nope."); } } // good let test; if (currentUser) { test = () => { console.log("Yup."); }; }
arguments 的優先級高于高于每個函數作用域自帶的 arguments 對象,這會導致函數自帶的 arguments 值被覆蓋。
// bad function foo(name, options, arguments) { // ... } // good function foo(name, options, args) { // ... }不要在函數體內使用 arguments,使用 ...rest 代替
eslint
prefer-rest-params
... 明確出你想用那個參數,同時,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
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;函數簽名部分要有空格
eslint
space-before-function-paren
space-before-blocks
// bad const f = function(){}; const g = function (){}; const h = function() {}; // good const f = function a() {};不修改參數
eslint
no-param-reassign
函數簽名時定義的參數,在函數體內不允許被重新賦值(包含參數本身,若參數為對象,還包括該對象所有屬性的值),
一個函數應該是沒有任何副作用的。
// 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
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]);若函數簽名包含多個參數需要使用多行,那就每行有且僅有一個參數
// 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
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; });如果函數體有且僅有一個沒有副作用的表達式,那么刪除大括號和 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 })); // 表達式有副作用就不要用隱式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
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; });避免箭頭函數(=>)和比較操作符(<=, >=)混淆.
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中強制約束函數體的位置, 就寫在箭頭后面
eslint
implicit-arrow-linebreak
// 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
no-useless-constructor
// 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
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
雖然一行是簡潔的,有一個明確的方式進口和一個明確的出口方式來保證一致性。
// bad export { es6 as default } from "./Component"; // good import { es6 } from "./Component"; export default es6;一個路徑只 import 一次
eslint
no-duplicate-imports
從同一個路徑下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
import/no-mutable-exports
變化通常都是需要避免,特別是當你要輸出可變的綁定。雖然在某些場景下可能需要這種技術,但總的來說應該導出常量。
// bad let foo = 3; export { foo } // good const foo = 3; export { foo }在一個單一導出模塊里,使用 export default
eslint
import/prefer-default-export
鼓勵使用更多文件,每個文件只做一件事情并導出,這樣可讀性和可維護性更好。
// bad export function foo() {} // good export default function foo() {}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();多行import應該縮進,就像多行數組和對象字面量
花括號與樣式指南中每個其他花括號塊遵循相同的縮進規則,逗號也是。
// 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高級函數代替 for-in、for-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
generator-star-spacing
它在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
dot-notation
這條,涉及一個曾經阿里出過一個看似簡單,實則很難的面試題,你就算猜對一個,你也不一定能說出原理:
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
no-restricted-properties
// bad const binary = Math.pow(2, 10); // good const binary = 2 ** 10;變量 永遠使用 const 或者 let,不使用 var
eslint
no-undef
prefer-const
// bad superPower = new SuperPower(); // good const superPower = new SuperPower();每一個變量都用一個 const 或者 let
eslint
one-var
扯蛋的理由:這種方式很容易去聲明新的變量,你不用去考慮把;調換成,,或者引入一個只有標點的不同的變化。真正的理由:做法也可以是你在調試的時候單步每個聲明語句,而不是一下跳過所有聲明。
// 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
鏈接變量分配會隱匿創建全局變量
// 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
no-plusplus
根據 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
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; // 參數定義了但未使用 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 聲明會將變量聲明提升到作用域的最前面,但是他的值卻只有在運行到代碼行時才會被賦值,永遠都使用 const 與 let,了解 時效區(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
eqeqeq
if 條件語句的強制 toBooleanif 條件語句的強制 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) { // ... }在 switch 的 case 與 default 分句中使用大括號創建語法聲明區域
eslint
no-case-declarations
語法聲明在整個 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
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;避免不必要的三元表達式
// 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
no-mixed-operators
開發者需要以最顯而易見的方式明確自己的意圖與邏輯
// 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
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 的關閉大括號在同一行
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 返回,那么后續的 else 就不需要寫了
如果 if 塊中包含 return,它后面的 else if 也包含了 return,那就應該把 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; } }控制語句 當你的控制語句 (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} 標簽名 // @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
space-comment
// 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; }積極使用 FIXME 與 TODO
當你的注釋需要向注釋閱讀者或者代碼的后繼開發者明確的表述一種期望時,應該積極使用 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
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", });關鍵字空格
eslint
keyword-spacing
在控制語句( if, while 等)的圓括號前空一格。在函數調用和定義時,參數列表和函數名之間不空格。
// bad if(isJedi) { fight (); } // good if (isJedi) { fight(); } // bad function fight () { console.log ("Swooosh!"); } // good function fight() { console.log("Swooosh!"); }用空格來隔開運算符
eslint
space-infix-ops
// bad const x=y+5; // good const x = y + 5;文件結尾加一個換行
eslint
eol-last
// bad function doSmth() { var foo = 2; }
// good function doSmth() { var foo = 2; }使用多行縮進的方式進行一個長方法鏈調用
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
為了確保代碼的人類可讀性與可維護性,代碼行應避免超過一定的長度
// 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
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];計算屬性內要空格
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]]調用函數時,函數名和小括號之間不要空格
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
避免出現連續多個空行,文件末尾只允許空一行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", };額外結尾逗號
eslint
comma-dangle
就算項目有可能運行在舊版本的瀏覽器中,但是像 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
semi
當 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
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 進行數字類型轉換
eslint
radix
使用 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
id-length
// bad function q() { // ... } // good function query() { // ... }使用小駝峰式命名對象、函數、實例
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的內容。這一概念可能會導致開發者誤以為更改這個不會導致崩潰或者不需要測試。 如果你想要什么東西變成“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.jsexport 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 = [ // ... ];使用全大寫字母設置靜態變量
導出變量
是 const 定義的, 保證不能被改變
這個變量是可信的,他的子屬性都是不能被改變的
一般我們都將項目的全局參數使用這種 全大寫+下劃線分隔的常量 來定義一些系統配置參數導出,比如 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 兼容性列表
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://m.specialneedsforspecialkids.com/yun/109711.html
摘要:這樣的變量增加了代碼量,并且混淆讀者。錯誤代碼示例變量雖然聲明了,但沒被使用持續更新 JavaScript 編碼規范 一、命名規范 1. 變量 命名方法:小駝峰式命名法(由小寫字母開始,后續每個單詞首字母都大寫) 命名建議:語義化的名詞 特殊:布爾值變量建議添加符合其含義的前綴動詞 is:是否 can:能不能 has:有沒有 示例: // 頁面標題 let pageT...
摘要:當然我們還可以引入框架,這些框架一般都自帶模板處理引擎,比如等語義化命名和語義化標簽我們盡量多采用語義化來命名,并且采用語義化標簽來書寫代碼,多用中新增的標簽來書寫。 1.黃金法則(Golden rule) 不管有多少人參與同一個項目,一定要確保每一行代碼都像是同一個人編寫的。 Every line of code should appear to be written by a si...
摘要:編碼規范是獨角獸公司內部的編碼規范,該項目是上很受歡迎的一個開源項目,在前端開發中使用廣泛,本文的配置規則就是以編碼規范和編碼規范作為基礎的。 更新時間:2019-01-22React.js create-react-app 項目 + VSCode 編輯器 + ESLint 代碼檢查工具 + Airbnb 編碼規范 前言 為什么要使用 ESLint 在項目開發過程中,編寫符合團隊編碼規...
摘要:用兩個空格代替制表符這是唯一能保證在所有環境下獲得一致展現的方法。編輯器配置將你的編輯器按照下面的配置進行設置,以免常見的代碼不一致和差異用兩個空格代替制表符保存文件時刪除尾部的空白符設置文件編碼為在文件結尾添加一個空白行。 黃金定律 永遠遵循同一套編碼規范 - 可以是這里列出的,也可以是你自己總結的。如果發現規范中有任何錯誤,敬請指正。 HTML 語法 用兩個空格代替制表符 (ta...
摘要:寫在前面對于不同的編程語言來說,具體的編碼規范各不相同,但是其宗旨都是一致的,就是保證代碼在高質量完成需求的同時具備良好的可讀性可維護性。減少標簽的數量編寫代碼時,盡量避免多余的父元素。 寫在前面 對于不同的編程語言來說,具體的編碼規范各不相同,但是其宗旨都是一致的,就是保證代碼在高質量完成需求的同時具備良好的可讀性、可維護性。 本文大部分內容來自網上,僅供個人參考學習! 網絡上的知...
摘要:六字符編碼通過明確聲明字符編碼,能夠確保瀏覽器快速并容易的判斷頁面內容的渲染方式。十一減少標簽的數量編寫代碼時,盡量避免多余的父元素。未完待續編寫靈活穩定高質量的代碼的規范閱讀更多 一、唯一定律 無論有多少人共同參與同一項目,一定要確保每一行代碼都像是唯一個人編寫的。 二、HTML 2.1 語法 (1)用兩個空格來代替制表符(tab) -- 這是唯一能保證在所有環境下獲得一致展現的方法...
閱讀 1668·2021-11-16 11:44
閱讀 2404·2021-10-11 11:07
閱讀 4064·2021-10-09 09:41
閱讀 673·2021-09-22 15:52
閱讀 3195·2021-09-09 09:33
閱讀 2712·2019-08-30 15:55
閱讀 2291·2019-08-30 15:55
閱讀 843·2019-08-30 15:55