Array - 数组
### 使用...来复制数值// badconst len = items.length;const itemsCopy = [];let i;for (i = 0; i < len; i += 1) { itemsCopy[i] = items[i];}**********************************************************************// goodconst itemsCopy = [...items];**********************************************************************### 将可迭代的对象转换为数组时,使用...而不是Array.from。const foo = document.querySelectorAll('.foo');// goodconst nodes = Array.from(foo);**********************************************************************// bestconst nodes = [...foo];**********************************************************************### 使用Array.from来转换类数组对象。const arrLike = { 0: 'foo', 1: 'bar', 2: 'baz', length: 3 };// badconst arr = Array.prototype.slice.call(arrLike);**********************************************************************// goodconst arr = Array.from(arrLike);**********************************************************************### 使用Array.from而非...来迭代, 因为这样可避免产生中间数组。// badconst baz = [...foo].map(bar);**********************************************************************// goodconst baz = Array.from(foo, bar);**********************************************************************### 在数组方法回调中使用return语句,在某些情况下可省略return。// bad - no returned value means `acc` becomes undefined after the first iteration[[0, 1], [2, 3], [4, 5]].reduce((acc, item, index) => { const flatten = acc.concat(item);});**********************************************************************// good[[0, 1], [2, 3], [4, 5]].reduce((acc, item, index) => { const flatten = acc.concat(item); return flatten;});**********************************************************************
Destructuring - 解构
### 在访问和使用对象的多个属性时,请使用对象分解,这样可以减少临时引用。// badfunction getFullName(user) { const firstName = user.firstName; const lastName = user.lastName; return `${firstName} ${lastName}`;}// goodfunction getFullName(user) { const { firstName, lastName } = user; return `${firstName} ${lastName}`;}**********************************************************************// bestfunction getFullName({ firstName, lastName }) { return `${firstName} ${lastName}`;}**********************************************************************### 使用数组解构const arr = [1, 2, 3, 4];// badconst first = arr[0];const second = arr[1];**********************************************************************// goodconst [first, second] = arr; //first-1,second-2。**********************************************************************### 用于返回值时使用对象分解,而非数组分解。// badfunction processInput(input) { // then a miracle occurs return [left, right, top, bottom];}// the caller needs to think about the order of return dataconst [left, __, top] = processInput(input);**********************************************************************// goodfunction processInput(input) { // then a miracle occurs return { left, right, top, bottom };}// the caller selects only the data they needconst { left, top } = processInput(input); //left, top 对应返回值里的left,top。**********************************************************************
String - 字符串
### 使用模板字符串,且避免使用多余空格。// badfunction sayHi(name) { return 'How are you, ' + name + '?';}// badfunction sayHi(name) { return ['How are you, ', name, '?'].join();}// badfunction sayHi(name) { return `How are you, ${ name }?`;}const [left, __, top] = processInput(input);**********************************************************************// goodfunction sayHi(name) { return `How are you, ${name}?`;}**********************************************************************
Functions - 函数
### 使用命名函数表达式而不是函数声明### 函数声明会提升,容易在定义函数前被调用-不利于可读性。(有利有弊)// badfunction foo() { // ...}// badconst foo = function () { // ...};**********************************************************************// good// lexical name distinguished from the variable-referenced invocation(s)const short = function longUniqueMoreDescriptiveLexicalFoo() { // ...};**********************************************************************### 函数立即执行时用括号包起来// immediately-invoked function expression (IIFE)(function () { console.log('Welcome to the Internet. Please follow me.');}());### Note: ECMA-262 defines a block as a list of statements.### A function declaration is not a statement.// badif (currentUser) { function test() { console.log('Nope.'); }}**********************************************************************// goodlet test;if (currentUser) { test = () => { console.log('Yup.'); };}**********************************************************************### 不要使用arguments,而是使用...,这样args是真数组,而不是伪数组。// badfunction concatenateAll() { const args = Array.prototype.slice.call(arguments); return args.join('');}**********************************************************************// goodfunction concatenateAll(...args) { return args.join('');}**********************************************************************### 使用默认参数,而非后期处理。// really badfunction handleThings(opts) { // No! We shouldn’t mutate function arguments. // Double bad: if opts is falsy it'll be set to an object which may // be what you want but it can introduce subtle bugs. opts = opts || {}; // ...}// still badfunction handleThings(opts) { if (opts === void 0) { opts = {}; } // ...}**********************************************************************// goodfunction handleThings(opts = {}) { // ...}**********************************************************************### 始终将默认参数放最后// badfunction handleThings(opts = {}, name) { // ...}**********************************************************************// goodfunction handleThings(name, opts = {}) { // ...}**********************************************************************### 请勿更改参数// badfunction f1(obj) { obj.key = 1;}**********************************************************************// goodfunction f2(obj) { const key = Object.prototype.hasOwnProperty.call(obj, 'key') ? obj.key : 1;}**********************************************************************### 请勿重新定义参数// badfunction f1(a) { a = 1; // ...}function f2(a) { if (!a) { a = 1; } // ...}**********************************************************************// goodfunction f3(a) { const b = a || 1; // ...}function f4(a = 1) { // ...}**********************************************************************### 使用...来调用可变参数函数。// badconst x = [1, 2, 3, 4, 5];console.log.apply(console, x);**********************************************************************// goodconst x = [1, 2, 3, 4, 5];console.log(...x); // 1 2 3 4 5// goodnew Date(...[2016, 8, 5]);**********************************************************************### 函数参数 (理想情况下应不超过 2 个)### 应避免三个以上参数的函数。通常情况下,参数超过两个意味着函数功能过于复杂,这时需要重新优化你的函数。### 当确实需要多个参数时,大多情况下可以考虑这些参数封装成一个对象。// badfunction createMenu(title, body, buttonText, cancellable) { ...}**********************************************************************// goodvar menuConfig = { title: 'Foo', body: 'Bar', buttonText: 'Baz', cancellable: true}function createMenu(menuConfig) { ...}**********************************************************************### 函数功能的单一性// badfunction emailClients(clients) { clients.forEach(client => { let clientRecord = database.lookup(client); if (clientRecord.isActive()) { email(client); } });}**********************************************************************// goodfunction emailClients(clients) { clients.forEach(client => { emailClientIfNeeded(client); });}function emailClientIfNeeded(client) { if (isClientActive(client)) { email(client); }}function isClientActive(client) { let clientRecord = database.lookup(client); return clientRecord.isActive();}**********************************************************************### 分解代码,移除重复代码。### 使用 Object.assign 设置默认对象。// badvar menuConfig = { title: null, body: 'Bar', buttonText: null, cancellable: true}function createMenu(config) { config.title = config.title || 'Foo' config.body = config.body || 'Bar' config.buttonText = config.buttonText || 'Baz' config.cancellable = config.cancellable === undefined ? config.cancellable : true;}createMenu(menuConfig);**********************************************************************// goodvar menuConfig = { title: 'Order', // User did not include 'body' key buttonText: 'Send', cancellable: true}function createMenu(config) { config = Object.assign({ title: 'Foo', body: 'Bar', buttonText: 'Baz', cancellable: true }, config); // config now equals: {title: "Order", body: "Bar", buttonText: "Send", cancellable: true} // ...}createMenu(menuConfig);**********************************************************************### 不要使用标记(Flag)作为函数参数// badfunction createFile(name, temp) { if (temp) { fs.create('./temp/' + name); } else { fs.create(name); }}**********************************************************************// goodfunction createTempFile(name) { fs.create('./temp/' + name);}----------function createFile(name) { fs.create(name);}**********************************************************************### 避免副作用### 当函数产生了除了“接受一个值并返回一个结果”之外的行为时,称该函数产生了副作用。### 应该将这些需要副作用的功能集中在一起,不要用多个函数/类修改某个文件。### 用且只用一个 service 完成这一需求。// bad// Global variable referenced by following function.// If we had another function that used this name, now it'd be an array and it could break it.var name = 'Ryan McDermott';function splitIntoFirstAndLastName() { name = name.split(' ');}splitIntoFirstAndLastName();console.log(name); // ['Ryan', 'McDermott'];**********************************************************************// goodfunction splitIntoFirstAndLastName(name) { return name.split(' ');}var name = 'Ryan McDermott'var newName = splitIntoFirstAndLastName(name);console.log(name); // 'Ryan McDermott';console.log(newName); // ['Ryan', 'McDermott'];**********************************************************************### 封装判断条件// badif (fsm.state === 'fetching' && isEmpty(listNode)) { /// ...}**********************************************************************// goodfunction shouldShowSpinner(fsm, listNode) { return fsm.state === 'fetching' && isEmpty(listNode);}if (shouldShowSpinner(fsmInstance, listNodeInstance)) { // ...}**********************************************************************### 避免“否定情况”的判断。//badfunction isDOMNodeNotPresent(node) { // ...}if (!isDOMNodeNotPresent(node)) { // ...}**********************************************************************// goodfunction isDOMNodePresent(node) { // ...}if (isDOMNodePresent(node)) { // ...}**********************************************************************### 避免过度优化// bad// 这里使用变量len是因为在老式浏览器中,// 直接使用正例中的方式会导致每次循环均重复计算list.length的值,// 而在现代浏览器中会自动完成优化,这一行为是没有必要的。for (var i = 0, len = list.length; i < len; i++) { // ...}**********************************************************************// goodfor (var i = 0; i < list.length; i++) { // ...}**********************************************************************### 当函数间存在相互调用的情况时,应将两者置于较近的位置。### 理想情况下,应将调用其他函数的函数写在被调用函数的上方。// badclass PerformanceReview { constructor(employee) { this.employee = employee; } lookupPeers() { return db.lookup(this.employee, 'peers'); } lookupMananger() { return db.lookup(this.employee, 'manager'); } getPeerReviews() { let peers = this.lookupPeers(); // ... } perfReview() { getPeerReviews(); getManagerReview(); getSelfReview(); } getManagerReview() { let manager = this.lookupManager(); } getSelfReview() { // ... }}let review = new PerformanceReview(user);review.perfReview();**********************************************************************// goodclass PerformanceReview { constructor(employee) { this.employee = employee; } perfReview() { getPeerReviews(); getManagerReview(); getSelfReview(); } getPeerReviews() { let peers = this.lookupPeers(); // ... } lookupPeers() { return db.lookup(this.employee, 'peers'); } getManagerReview() { let manager = this.lookupManager(); } lookupMananger() { return db.lookup(this.employee, 'manager'); } getSelfReview() { // ... }}let review = new PerformanceReview(employee);review.perfReview();**********************************************************************
Arrow Functions - 箭头函数
### 省略{}与使用{}。**********************************************************************// good[1, 2, 3].map((number) => `A string containing the ${number + 1}.`);// good[1, 2, 3].map((number) => { const nextNumber = number + 1; return `A string containing the ${nextNumber}.`;});// good[1, 2, 3].map((number, index) => ({ [index]: number,}));**********************************************************************### 如果表达式跨越多行,请将其括在括号中以提高可读性。// bad['get', 'post', 'put'].map((httpMethod) => Object.prototype.hasOwnProperty.call( httpMagicObjectWithAVeryLongName, httpMethod, ));**********************************************************************// good['get', 'post', 'put'].map((httpMethod) => ( Object.prototype.hasOwnProperty.call( httpMagicObjectWithAVeryLongName, httpMethod, )));**********************************************************************### 在参数周围加上括号。// bad[1, 2, 3].map(x => x * x);**********************************************************************// good[1, 2, 3].map((x) => x * x);**********************************************************************### 避免将箭头函数语避免将箭头函数语法(=>)与比较运算符(<=,>=)混淆。// badconst itemHeight = (item) => item.height >= 256 ? item.largeSize : item.smallSize;**********************************************************************// goodconst itemHeight = (item) => (item.height <= 256 ? item.largeSize : item.smallSize);**********************************************************************
Classes & Constructors - 类和构造函数
### 始终使用class,避免直接使用prototype。// badfunction Queue(contents = []) { this.queue = [...contents];}Queue.prototype.pop = function () { const value = this.queue[0]; this.queue.splice(0, 1); return value;};**********************************************************************// goodclass Queue { constructor (contents = []) { this.queue = [...contents]; } pop () { const value = this.queue[0]; this.queue.splice(0, 1); return value; }}**********************************************************************### 使用extends继承。// badconst inherits = require('inherits');function PeekableQueue(contents) { Queue.apply(this, contents);}inherits(PeekableQueue, Queue);PeekableQueue.prototype.peek = function () { return this.queue[0];};**********************************************************************// goodclass PeekableQueue extends Queue { peek() { return this.queue[0]; }}**********************************************************************### 方法可以返回this以帮助方法链式调用。### 注意this,文章https://blog.csdn.net/zhjzls/article/details/80038353// badJedi.prototype.jump = function () { this.jumping = true; return true;};Jedi.prototype.setHeight = function (height) { this.height = height;};const luke = new Jedi();luke.jump(); // => trueluke.setHeight(20); // => undefined**********************************************************************// goodclass Jedi { jump() { this.jumping = true; return this; } setHeight(height) { this.height = height; return this; }}const luke = new Jedi();luke.jump() .setHeight(20); // important!**********************************************************************### 类的设计原则s### 单一职责原则(SRP)### 最小化对一个类需要修改的次数是非常有必要的,一个类里不要有太多太杂的功能。// badclass UserSettings { constructor(user) { this.user = user; } changeSettings(settings) { if (this.verifyCredentials(user)) { // ... } } verifyCredentials(user) { // ... }}**********************************************************************//goodclass UserAuth { constructor(user) { this.user = user; } verifyCredentials() { // ... }}class UserSettings { constructor(user) { this.user = user; this.auth = new UserAuth(user) } changeSettings(settings) { if (this.auth.verifyCredentials()) { // ... } }}**********************************************************************### 开/闭原则(OCP)### 易于扩展,难于修改。// badclass AjaxRequester { constructor() { // What if we wanted another HTTP Method, like DELETE? We would have to // open this file up and modify this and put it in manually. this.HTTP_METHODS = ['POST', 'PUT', 'GET']; } get(url) { // ... }}**********************************************************************// goodclass AjaxRequester { constructor() { this.HTTP_METHODS = ['POST', 'PUT', 'GET']; } get(url) { // ... } addHTTPMethod(method) { this.HTTP_METHODS.push(method); }}**********************************************************************### 利斯科夫替代原则 (LSP)### 子类对象应该能够替换其超类对象被使用### 如果有一个父类和一个子类,当采用子类替换父类时不应该产生错误的结果。// badclass Rectangle { constructor() { this.width = 0; this.height = 0; } setColor(color) { // ... } render(area) { // ... } setWidth(width) { this.width = width; } setHeight(height) { this.height = height; } getArea() { return this.width * this.height; }}class Square extends Rectangle { constructor() { super(); } setWidth(width) { this.width = width; this.height = width; } setHeight(height) { this.width = height; this.height = height; }}function renderLargeRectangles(rectangles) { rectangles.forEach((rectangle) => { rectangle.setWidth(4); rectangle.setHeight(5); let area = rectangle.getArea(); // BAD: Will return 25 for Square. Should be 20. rectangle.render(area); })}let rectangles = [new Rectangle(), new Rectangle(), new Square()];renderLargeRectangles(rectangles);**********************************************************************// goodclass Shape { constructor() {} setColor(color) { // ... } render(area) { // ... }}class Rectangle extends Shape { constructor() { super(); this.width = 0; this.height = 0; } setWidth(width) { this.width = width; } setHeight(height) { this.height = height; } getArea() { return this.width * this.height; }}class Square extends Shape { constructor() { super(); this.length = 0; } setLength(length) { this.length = length; } getArea() { return this.length * this.length; }}function renderLargeShapes(shapes) { shapes.forEach((shape) => { switch (shape.constructor.name) { case 'Square': shape.setLength(5); case 'Rectangle': shape.setWidth(4); shape.setHeight(5); } let area = shape.getArea(); shape.render(area); })}let shapes = [new Rectangle(), new Rectangle(), new Square()];renderLargeShapes(shapes);**********************************************************************### 接口隔离原则 (ISP)### 客户端不应该依赖它不需要的接口,一个类对另一个类的依赖应该建立在最小的接口上。### 减少对配置参数数量的需求是有益的。// badclass DOMTraverser { constructor(settings) { this.settings = settings; this.setup(); } setup() { this.rootNode = this.settings.rootNode; this.animationModule.setup(); } traverse() { // ... }}let $ = new DOMTraverser({ rootNode: document.getElementsByTagName('body'), animationModule: function() {} // Most of the time, we won't need to animate when traversing. // ...});**********************************************************************// goodclass DOMTraverser { constructor(settings) { this.settings = settings; this.options = settings.options; this.setup(); } setup() { this.rootNode = this.settings.rootNode; this.setupOptions(); } setupOptions() { if (this.options.animationModule) { // ... } } traverse() { // ... }}let $ = new DOMTraverser({ rootNode: document.getElementsByTagName('body'), options: { animationModule: function() {} }});**********************************************************************### 依赖反转原则 (DIP)### 两个核心点:高层模块不应该依赖于低层模块。他们都应该依赖于抽象接口。### 抽象接口应该脱离具体实现,具体实现应该依赖于抽象接口。// badclass InventoryTracker { constructor(items) { this.items = items; // BAD: We have created a dependency on a specific request implementation. // We should just have requestItems depend on a request method: `request` this.requester = new InventoryRequester(); } requestItems() { this.items.forEach((item) => { this.requester.requestItem(item); }); }}class InventoryRequester { constructor() { this.REQ_METHODS = ['HTTP']; } requestItem(item) { // ... }}let inventoryTracker = new InventoryTracker(['apples', 'bananas']);inventoryTracker.requestItems();**********************************************************************// goodclass InventoryTracker { constructor(items, requester) { this.items = items; this.requester = requester; } requestItems() { this.items.forEach((item) => { this.requester.requestItem(item); }); }}class InventoryRequesterV1 { constructor() { this.REQ_METHODS = ['HTTP']; } requestItem(item) { // ... }}class InventoryRequesterV2 { constructor() { this.REQ_METHODS = ['WS']; } requestItem(item) { // ... }}// By constructing our dependencies externally and injecting them, we can easily// substitute our request module for a fancy new one that uses WebSockets.let inventoryTracker = new InventoryTracker(['apples', 'bananas'], new InventoryRequesterV2());inventoryTracker.requestItems();**********************************************************************
Modules - 模块
### 不要使用通配符导入### Why? This makes sure you have a single default export.// badimport * as AirbnbStyleGuide from './AirbnbStyleGuide';**********************************************************************// goodimport AirbnbStyleGuide from './AirbnbStyleGuide';**********************************************************************### 请勿直接在import里直接export。// bad// filename es6.js. !!! Not AirbnbStyleGuide.js !!!export { es6 as default } from './AirbnbStyleGuide';**********************************************************************// good// filename es6.jsimport { es6 } from './AirbnbStyleGuide';export default es6;**********************************************************************### 一个地方一个import// badimport foo from 'foo';// … some other imports … //import { named1, named2 } from 'foo';**********************************************************************// goodimport foo, { named1, named2 } from 'foo';// goodimport foo, { named1, named2,} from 'foo';**********************************************************************### 避免导出变量,一般来说导出常量。// badlet foo = 3;export { foo };**********************************************************************// goodconst foo = 3;export { foo };**********************************************************************### 在具有单个导出的模块中,首选默认导出而不是命名导出。// badexport function foo() {}**********************************************************************// goodexport default function foo() {}**********************************************************************
Iterators and Generators - 迭代器和生成器
### 不要是使用for-in or for-of这种迭代器### 尽量使用高阶函数来遍历### map()/ every()/ filter()/ find()/ findIndex()/ reduce()/ some()/ ... 等遍历数组### Object.keys()/ Object.values()/ Object.entries()产生数组,以便可以遍历对象。const numbers = [1, 2, 3, 4, 5];// badlet sum = 0;for (let num of numbers) { sum += num;}sum === 15;// goodlet 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;**********************************************************************// badconst increasedByOne = [];for (let i = 0; i < numbers.length; i++) { increasedByOne.push(numbers[i] + 1);}// goodconst increasedByOne = [];numbers.forEach((num) => { increasedByOne.push(num + 1);});**********************************************************************// best (keeping it functional)const increasedByOne = numbers.map((num) => num + 1);**********************************************************************### 现在不要使用生成器。
Properties - 属性
### 访问属性时使用。### 使用[]访问变量属性。### 计算指数时使用指数运算符 ** 。// badconst binary = Math.pow(2, 10);**********************************************************************// goodconst binary = 2 ** 10;**********************************************************************
Variables - 变量
### 使用const or let声明变量。### 一个const or let声明一个变量。### const写一块,let写一块。### 在需要的地方分配变量,但将其放置在合理的位置### Why?let并且const是块作用域而不是函数作用域。// bad - unnecessary function callfunction checkName(hasName) { const name = getName(); if (hasName === 'test') { return false; } if (name === 'test') { this.setName(''); return false; } return name;}**********************************************************************// goodfunction checkName(hasName) { if (hasName === 'test') { return false; } const name = getName(); if (name === 'test') { this.setName(''); return false; } return name;}**********************************************************************### 不要链式声明变量。### 可能会隐式创建全局变量。// bad(function example() { // JavaScript interprets this as // let a = ( b = ( c = 1 ) ); // The let keyword only applies to variable a; variables b and c become // global variables. let a = b = c = 1;}());console.log(a); // throws ReferenceErrorconsole.log(b); // 1console.log(c); // 1**********************************************************************// good(function example() { let a = 1; let b = a; let c = a;}());console.log(a); // throws ReferenceErrorconsole.log(b); // throws ReferenceErrorconsole.log(c); // throws ReferenceError**********************************************************************// the same applies for `const`### 不要使用 ++ 和 -- ,而是使用 += 和 -= 。### 不要在声明的 = 后折行,若超过max-len则将 = 后的内容将括号括起来。### 使用易于检索的名称// bad// 525600 是什么?for (var i = 0; i < 525600; i++) { runCronJob();}**********************************************************************// good// Declare them as capitalized `var` globals.var MINUTES_IN_A_YEAR = 525600;for (var i = 0; i < MINUTES_IN_A_YEAR; i++) { runCronJob();}**********************************************************************### 避免无意义的条件判断// badfunction createMicrobrewery(name) { var breweryName; if (name) { breweryName = name; } else { breweryName = 'Hipster Brew Co.'; }}**********************************************************************//goodfunction createMicrobrewery(name) { var breweryName = name || 'Hipster Brew Co.'}**********************************************************************
Hoisting - 提升
### var 声明提升到当前作用域顶部,赋值不提升。### const 和 let 不提升, Temporal Dead Zones (TDZ)。### 匿名函数表达式声明提升,赋值不提升。function example() { console.log(anonymous); // => undefined anonymous(); // => TypeError anonymous is not a function var anonymous = function () { //匿名!!!Attention!!! 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'); };}// the same is true when the function name// is the same as the variable name.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'); }}
Comparison Operators & Equality - 比较运算符和等号
### 空数组空对象都是trueif ([0] && []) { // true // an array (even an empty one) is an object, objects will evaluate to true}### if(value) 将这种快捷方式用于布尔值,数字和字符串进行显示对比// badif (isValid === true) { // ...}**********************************************************************// goodif (isValid) { // ...}**********************************************************************// badif (name) { // ...}**********************************************************************// goodif (name !== '') { // ...}**********************************************************************// badif (collection.length) { // ...}**********************************************************************// goodif (collection.length > 0) { // ...}**********************************************************************### 使用switch时,用{}包裹对case的处理。// badswitch (foo) { case 1: let x = 1; break; default: class C {}}**********************************************************************// goodswitch (foo) { case 1: { let x = 1; break; } case 4: bar(); break; default: { class C {} }}**********************************************************************### 三元运算不嵌套,单行处理。// badconst foo = maybe1 > maybe2 ? "bar" : value1 > value2 ? "baz" : null;// best// split into 2 separated ternary expressionsconst maybeNull = value1 > value2 ? 'baz' : null;const foo = maybe1 > maybe2 ? 'bar' : maybeNull;### 避免不必要的声明语句。// badconst foo = a ? a : b;const bar = c ? true : false;const baz = c ? false : true;**********************************************************************// goodconst foo = a || b;const bar = !!c;const baz = !c;**********************************************************************### 混合运算时将优先级模糊的地方使用括号括起来。
WhiteSpace - 空白符
### 在块和下一条语句之间保留空白行。// badconst obj = { foo() { }, bar() { },};return obj;**********************************************************************// goodconst obj = { foo() { }, bar() { },};return obj;**********************************************************************### 不要用空行填充块。// badclass Foo { constructor(bar) { this.bar = bar; }}**********************************************************************// goodfunction bar() { console.log(foo);}**********************************************************************### 括号内不要使用空格,括号前后空格在不同情况下有不同选择。// badif ( foo ) { console.log(foo);}**********************************************************************// goodif (foo) { console.log(foo);}**********************************************************************// badfunction apple (x) { // ...}**********************************************************************// goodfunction apple(x) { // ...}**********************************************************************### 注意方括号中的空格,括号前后不需要空格,逗号后需要空格,逗号前避免出现空格。// badconst foo = [ 1, 2, 3 ];console.log(foo[ 0 ]);**********************************************************************// goodconst foo = [1, 2, 3];console.log(foo[0]);**********************************************************************### 注意花括号中的空格,花括号需要空格,冒号前不需要空格。// badconst foo = {clark: 'kent'};**********************************************************************// goodconst foo = { clark: 'kent' };**********************************************************************
Comma - 逗号
### 在尾部添加额外逗号。### Why?这使得git diff更干净。同样,像Babel这样的编译器也将删除已编译代码中的多余尾部逗号。### Attention!逗号不要在...rest后出现。// badcreateHero( firstName, lastName, inventorOf);**********************************************************************// goodcreateHero( firstName, lastName, inventorOf,);// good (note that a comma must not appear after a "rest" element)createHero( firstName, lastName, inventorOf, ...heroArgs);**********************************************************************
Type Casting & Coercion - 类型转换
### 转换为字符串// badconst totalScore = new String(this.reviewScore); // typeof totalScore is "object" not "string"// badconst totalScore = this.reviewScore + ''; // invokes this.reviewScore.valueOf()// badconst totalScore = this.reviewScore.toString(); // isn’t guaranteed to return a string**********************************************************************// goodconst totalScore = String(this.reviewScore);**********************************************************************### 转换为数字const inputValue = '4';// badconst val = new Number(inputValue);// badconst val = +inputValue;// badconst val = inputValue >> 0;// badconst val = parseInt(inputValue);**********************************************************************// goodconst val = Number(inputValue);// goodconst val = parseInt(inputValue, 10);**********************************************************************### 转为布尔值const age = 0;// badconst hasAge = new Boolean(age);// goodconst hasAge = Boolean(age);**********************************************************************// bestconst hasAge = !!age;**********************************************************************
Naming Conventions - 命名规范
### 在命名对象,函数和实例时,请使用camelCase(驼峰)。### 仅在命名构造函数或类时使用PascalCase(大驼峰)。### 不要保存对的引用this。使用箭头函数或Function#bind。### 箭头函数没有this,如果箭头函数里出现了this,此this一定代表外层的this。// badfunction foo() { const self = this; return function () { console.log(self); };}// badfunction foo() { const that = this; return function () { console.log(that); };}**********************************************************************// goodfunction foo() { return () => { console.log(this); };}**********************************************************************### 基本文件名应与默认导出文件名完全匹配。// file 1 contentsclass CheckBox { // ...}export default CheckBox;// file 2 contentsexport default function fortyTwo() { return 42; }// file 3 contentsexport default function insideDirectory() {}// in some other file// badimport CheckBox from './checkBox'; // PascalCase import/export, camelCase filenameimport FortyTwo from './FortyTwo'; // PascalCase import/filename, camelCase exportimport InsideDirectory from './InsideDirectory'; // PascalCase import/filename, camelCase export// badimport CheckBox from './check_box'; // PascalCase import/export, snake_case filenameimport forty_two from './forty_two'; // snake_case import/filename, camelCase exportimport inside_directory from './inside_directory'; // snake_case import, camelCase exportimport index from './inside_directory/index'; // requiring the index file explicitlyimport insideDirectory from './insideDirectory/index'; // requiring the index file explicitly**********************************************************************// goodimport CheckBox from './CheckBox'; // PascalCase export/import/filenameimport fortyTwo from './fortyTwo'; // camelCase export/import/filenameimport insideDirectory from './insideDirectory'; // camelCase export/import/directory name/implicit "index"// ^ supports both insideDirectory.js and insideDirectory/index.js**********************************************************************### 导出默认功能时,请使用camelCase,文件名应与函数名称相同。function makeStyleGuide() { // ...}export default makeStyleGuide;### 在导出constructor/class/singleton/function library/bare object时使用PascalCase。### 首字母缩写词和首字母缩写应始终全部大写或全部小写(提高可读性)。// badimport SmsContainer from './containers/SmsContainer';// badconst HttpRequests = [ // ...];**********************************************************************// goodimport SMSContainer from './containers/SMSContainer';// goodconst HTTPRequests = [ // ...];// also goodconst httpRequests = [ // ...];**********************************************************************// bestimport TextMessageContainer from './containers/TextMessageContainer';// bestconst requests = [ // ...];**********************************************************************### const在已导出,或嵌套永不更改时变量名能大写。// badconst PRIVATE_VARIABLE = 'should not be unnecessarily uppercased within a file';// badexport const THING_TO_BE_CHANGED = 'should obviously not be uppercased';// badexport let REASSIGNABLE_VARIABLE = 'do not use let with uppercase variables';// ---// allowed but does not supply semantic valueexport const apiKey = 'SOMEKEY';**********************************************************************// better in most casesexport const API_KEY = 'SOMEKEY';**********************************************************************// ---// bad - unnecessarily uppercases key while adding no semantic valueexport const MAPPING = { KEY: 'value'};**********************************************************************// goodexport const MAPPING = { key: 'value'};**********************************************************************
Standard Library - 标准库
### 使用 Number.isNaN 而不是全局的 isNaN。### 与原有的isNaN()方法不同,不存在隐式的Number()类型转换,非NaN一律返回false。// badisNaN('1.2.3'); // true**********************************************************************// goodNumber.isNaN('1.2.3'); // false,没有隐式转换所以非NaN,返回false。Number.isNaN(Number('1.2.3')); // true**********************************************************************### 使用 Number.isFinite 而不是全局的 isFinite。### 与原有的isFinite()方法的不同之处在于,### Number.isFinite()方法没有隐式的Number()类型转换,对于非数值一律返回false。// badisFinite('2e3'); // true**********************************************************************// goodNumber.isFinite('2e3'); // false,没有隐式转换,所以返回false。Number.isFinite(parseInt('2e3', 10)); // true**********************************************************************
Objects and Data Structures - 对象和数据结构
### 使用 getters 和 setters// bad// 通过引用直接修改值class BankAccount { constructor() { this.balance = 1000; }}let bankAccount = new BankAccount();// Buy shoes...bankAccount.balance = bankAccount.balance - 100;**********************************************************************// good// 通过setter修改值class BankAccount { constructor() { this.balance = 1000; } // It doesn't have to be prefixed with `get` or `set` to be a getter/setter withdraw(amount) { if (verifyAmountCanBeDeducted(amount)) { this.balance -= amount; } }}let bankAccount = new BankAccount();// Buy shoes...bankAccount.withdraw(100);**********************************************************************### 让对象拥有私有成员### 可以通过闭包完成
Concurrency - 并发
### 用 Promises 替代回调// badrequire('request').get('https://en.wikipedia.org/wiki/Robert_Cecil_Martin', function(err, response) { if (err) { console.error(err); } else { require('fs').writeFile('article.html', response.body, function(err) { if (err) { console.error(err); } else { console.log('File written'); } }) }})**********************************************************************// goodrequire('request-promise').get('https://en.wikipedia.org/wiki/Robert_Cecil_Martin') .then(function(response) { return require('fs-promise').writeFile('article.html', response); }) .then(function() { console.log('File written'); }) .catch(function(err) { console.error(err); })**********************************************************************### Async/Await 是较 Promises 更好的选择// badrequire('request-promise').get('https://en.wikipedia.org/wiki/Robert_Cecil_Martin') .then(function(response) { return require('fs-promise').writeFile('article.html', response); }) .then(function() { console.log('File written'); }) .catch(function(err) { console.error(err); })**********************************************************************// goodimport { get } from "request-promise";import { writeFile } from "fs-extra";async function getCleanCodeArticle() { try { const body = await get( "https://en.wikipedia.org/wiki/Robert_Cecil_Martin" ); await writeFile("article.html", body); console.log("File written"); } catch (err) { console.error(err); }}getCleanCodeArticle()**********************************************************************