上面两篇文章的整合,建议去看原文,此笔记自留。
### 使用...来复制数值
// bad
const len = items.length;
const itemsCopy = [];
let i;
for (i = 0; i < len; i += 1) {
itemsCopy[i] = items[i];
}
**********************************************************************
// good
const itemsCopy = [...items];
**********************************************************************
### 将可迭代的对象转换为数组时,使用...而不是Array.from。
const foo = document.querySelectorAll('.foo');
// good
const nodes = Array.from(foo);
**********************************************************************
// best
const nodes = [...foo];
**********************************************************************
### 使用Array.from来转换类数组对象。
const arrLike = { 0: 'foo', 1: 'bar', 2: 'baz', length: 3 };
// bad
const arr = Array.prototype.slice.call(arrLike);
**********************************************************************
// good
const arr = Array.from(arrLike);
**********************************************************************
### 使用Array.from而非...来迭代, 因为这样可避免产生中间数组。
// bad
const baz = [...foo].map(bar);
**********************************************************************
// good
const 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;
});
**********************************************************************
### 在访问和使用对象的多个属性时,请使用对象分解,这样可以减少临时引用。
// 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}`;
}
**********************************************************************
### 使用数组解构
const arr = [1, 2, 3, 4];
// bad
const first = arr[0];
const second = arr[1];
**********************************************************************
// good
const [first, second] = arr; //first-1,second-2。
**********************************************************************
### 用于返回值时使用对象分解,而非数组分解。
// bad
function processInput(input) {
// then a miracle occurs
return [left, right, top, bottom];
}
// the caller needs to think about the order of return data
const [left, __, top] = processInput(input);
**********************************************************************
// good
function processInput(input) {
// then a miracle occurs
return { left, right, top, bottom };
}
// the caller selects only the data they need
const { left, top } = processInput(input); //left, top 对应返回值里的left,top。
**********************************************************************
### 使用模板字符串,且避免使用多余空格。
// bad
function sayHi(name) {
return 'How are you, ' + name + '?';
}
// bad
function sayHi(name) {
return ['How are you, ', name, '?'].join();
}
// bad
function sayHi(name) {
return `How are you, ${ name }?`;
}
const [left, __, top] = processInput(input);
**********************************************************************
// good
function sayHi(name) {
return `How are you, ${name}?`;
}
**********************************************************************
### 使用命名函数表达式而不是函数声明
### 函数声明会提升,容易在定义函数前被调用-不利于可读性。(有利有弊)
// bad
function foo() {
// ...
}
// bad
const 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.
// bad
if (currentUser) {
function test() {
console.log('Nope.');
}
}
**********************************************************************
// good
let test;
if (currentUser) {
test = () => {
console.log('Yup.');
};
}
**********************************************************************
### 不要使用arguments,而是使用...,这样args是真数组,而不是伪数组。
// bad
function concatenateAll() {
const args = Array.prototype.slice.call(arguments);
return args.join('');
}
**********************************************************************
// good
function concatenateAll(...args) {
return args.join('');
}
**********************************************************************
### 使用默认参数,而非后期处理。
// really bad
function handleThings(opts) {
// No! We shouldn’t mutate function arguments.
// Double bad: if opts is falsy it'll be set to an object which may
// be what you want but it can introduce subtle bugs.
opts = opts || {};
// ...
}
// still bad
function handleThings(opts) {
if (opts === void 0) {
opts = {};
}
// ...
}
**********************************************************************
// good
function handleThings(opts = {}) {
// ...
}
**********************************************************************
### 始终将默认参数放最后
// bad
function handleThings(opts = {}, name) {
// ...
}
**********************************************************************
// good
function handleThings(name, opts = {}) {
// ...
}
**********************************************************************
### 请勿更改参数
// bad
function f1(obj) {
obj.key = 1;
}
**********************************************************************
// good
function f2(obj) {
const key = Object.prototype.hasOwnProperty.call(obj, 'key') ? obj.key : 1;
}
**********************************************************************
### 请勿重新定义参数
// bad
function f1(a) {
a = 1;
// ...
}
function f2(a) {
if (!a) { a = 1; }
// ...
}
**********************************************************************
// good
function f3(a) {
const b = a || 1;
// ...
}
function f4(a = 1) {
// ...
}
**********************************************************************
### 使用...来调用可变参数函数。
// bad
const x = [1, 2, 3, 4, 5];
console.log.apply(console, x);
**********************************************************************
// good
const x = [1, 2, 3, 4, 5];
console.log(...x); // 1 2 3 4 5
// good
new Date(...[2016, 8, 5]);
**********************************************************************
### 函数参数 (理想情况下应不超过 2 个)
### 应避免三个以上参数的函数。通常情况下,参数超过两个意味着函数功能过于复杂,这时需要重新优化你的函数。
### 当确实需要多个参数时,大多情况下可以考虑这些参数封装成一个对象。
// bad
function createMenu(title, body, buttonText, cancellable) {
...
}
**********************************************************************
// good
var menuConfig = {
title: 'Foo',
body: 'Bar',
buttonText: 'Baz',
cancellable: true
}
function createMenu(menuConfig) {
...
}
**********************************************************************
### 函数功能的单一性
// bad
function emailClients(clients) {
clients.forEach(client => {
let clientRecord = database.lookup(client);
if (clientRecord.isActive()) {
email(client);
}
});
}
**********************************************************************
// good
function 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 设置默认对象。
// bad
var 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);
**********************************************************************
// good
var 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)作为函数参数
// bad
function createFile(name, temp) {
if (temp) {
fs.create('./temp/' + name);
} else {
fs.create(name);
}
}
**********************************************************************
// good
function 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'];
**********************************************************************
// good
function splitIntoFirstAndLastName(name) {
return name.split(' ');
}
var name = 'Ryan McDermott'
var newName = splitIntoFirstAndLastName(name);
console.log(name); // 'Ryan McDermott';
console.log(newName); // ['Ryan', 'McDermott'];
**********************************************************************
### 封装判断条件
// bad
if (fsm.state === 'fetching' && isEmpty(listNode)) {
/// ...
}
**********************************************************************
// good
function shouldShowSpinner(fsm, listNode) {
return fsm.state === 'fetching' && isEmpty(listNode);
}
if (shouldShowSpinner(fsmInstance, listNodeInstance)) {
// ...
}
**********************************************************************
### 避免“否定情况”的判断。
//bad
function isDOMNodeNotPresent(node) {
// ...
}
if (!isDOMNodeNotPresent(node)) {
// ...
}
**********************************************************************
// good
function isDOMNodePresent(node) {
// ...
}
if (isDOMNodePresent(node)) {
// ...
}
**********************************************************************
### 避免过度优化
// bad
// 这里使用变量len是因为在老式浏览器中,
// 直接使用正例中的方式会导致每次循环均重复计算list.length的值,
// 而在现代浏览器中会自动完成优化,这一行为是没有必要的。
for (var i = 0, len = list.length; i < len; i++) {
// ...
}
**********************************************************************
// good
for (var i = 0; i < list.length; i++) {
// ...
}
**********************************************************************
### 当函数间存在相互调用的情况时,应将两者置于较近的位置。
### 理想情况下,应将调用其他函数的函数写在被调用函数的上方。
// bad
class 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();
**********************************************************************
// good
class 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();
**********************************************************************
### 省略{}与使用{}。
**********************************************************************
// 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);
**********************************************************************
### 避免将箭头函数语避免将箭头函数语法(=>)与比较运算符(<=,>=)混淆。
// bad
const itemHeight = (item) => item.height >= 256 ? item.largeSize : item.smallSize;
**********************************************************************
// good
const itemHeight = (item) => (item.height <= 256 ? item.largeSize : item.smallSize);
**********************************************************************
### 始终使用class,避免直接使用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继承。
// 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以帮助方法链式调用。
### 注意this,文章https://blog.csdn.net/zhjzls/article/details/80038353
// 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); // important!
**********************************************************************
### 类的设计原则s
### 单一职责原则(SRP)
### 最小化对一个类需要修改的次数是非常有必要的,一个类里不要有太多太杂的功能。
// bad
class UserSettings {
constructor(user) {
this.user = user;
}
changeSettings(settings) {
if (this.verifyCredentials(user)) {
// ...
}
}
verifyCredentials(user) {
// ...
}
}
**********************************************************************
//good
class 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)
### 易于扩展,难于修改。
// bad
class 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) {
// ...
}
}
**********************************************************************
// good
class AjaxRequester {
constructor() {
this.HTTP_METHODS = ['POST', 'PUT', 'GET'];
}
get(url) {
// ...
}
addHTTPMethod(method) {
this.HTTP_METHODS.push(method);
}
}
**********************************************************************
### 利斯科夫替代原则 (LSP)
### 子类对象应该能够替换其超类对象被使用
### 如果有一个父类和一个子类,当采用子类替换父类时不应该产生错误的结果。
// bad
class 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);
**********************************************************************
// good
class 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)
### 客户端不应该依赖它不需要的接口,一个类对另一个类的依赖应该建立在最小的接口上。
### 减少对配置参数数量的需求是有益的。
// bad
class 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.
// ...
});
**********************************************************************
// good
class 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)
### 两个核心点:高层模块不应该依赖于低层模块。他们都应该依赖于抽象接口。
### 抽象接口应该脱离具体实现,具体实现应该依赖于抽象接口。
// bad
class 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();
**********************************************************************
// good
class 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();
**********************************************************************
### 不要使用通配符导入
### Why? This makes sure you have a single default export.
// bad
import * as AirbnbStyleGuide from './AirbnbStyleGuide';
**********************************************************************
// good
import AirbnbStyleGuide from './AirbnbStyleGuide';
**********************************************************************
### 请勿直接在import里直接export。
// bad
// filename es6.js. !!! Not AirbnbStyleGuide.js !!!
export { es6 as default } from './AirbnbStyleGuide';
**********************************************************************
// good
// filename es6.js
import { es6 } from './AirbnbStyleGuide';
export default es6;
**********************************************************************
### 一个地方一个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';
**********************************************************************
### 避免导出变量,一般来说导出常量。
// bad
let foo = 3;
export { foo };
**********************************************************************
// good
const foo = 3;
export { foo };
**********************************************************************
### 在具有单个导出的模块中,首选默认导出而不是命名导出。
// bad
export function foo() {}
**********************************************************************
// good
export default function foo() {}
**********************************************************************
### 不要是使用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];
// 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);
**********************************************************************
### 现在不要使用生成器。
### 访问属性时使用。
### 使用[]访问变量属性。
### 计算指数时使用指数运算符 ** 。
// bad
const binary = Math.pow(2, 10);
**********************************************************************
// good
const binary = 2 ** 10;
**********************************************************************
### 使用const or let声明变量。
### 一个const or let声明一个变量。
### const写一块,let写一块。
### 在需要的地方分配变量,但将其放置在合理的位置
### Why?let并且const是块作用域而不是函数作用域。
// bad - unnecessary function call
function checkName(hasName) {
const name = getName();
if (hasName === 'test') {
return false;
}
if (name === 'test') {
this.setName('');
return false;
}
return name;
}
**********************************************************************
// good
function checkName(hasName) {
if (hasName === 'test') {
return false;
}
const name = getName();
if (name === 'test') {
this.setName('');
return false;
}
return name;
}
**********************************************************************
### 不要链式声明变量。
### 可能会隐式创建全局变量。
// bad
(function example() {
// JavaScript interprets this as
// let a = ( b = ( c = 1 ) );
// The let keyword only applies to variable a; variables b and c become
// global variables.
let a = b = c = 1;
}());
console.log(a); // throws ReferenceError
console.log(b); // 1
console.log(c); // 1
**********************************************************************
// good
(function example() {
let a = 1;
let b = a;
let c = a;
}());
console.log(a); // throws ReferenceError
console.log(b); // throws ReferenceError
console.log(c); // throws ReferenceError
**********************************************************************
// the same applies for `const`
### 不要使用 ++ 和 -- ,而是使用 += 和 -= 。
### 不要在声明的 = 后折行,若超过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();
}
**********************************************************************
### 避免无意义的条件判断
// bad
function createMicrobrewery(name) {
var breweryName;
if (name) {
breweryName = name;
} else {
breweryName = 'Hipster Brew Co.';
}
}
**********************************************************************
//good
function createMicrobrewery(name) {
var breweryName = name || 'Hipster Brew Co.'
}
**********************************************************************
### 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');
}
}
### 空数组空对象都是true
if ([0] && []) {
// true
// an array (even an empty one) is an object, objects will evaluate to true
}
### if(value) 将这种快捷方式用于布尔值,数字和字符串进行显示对比
// bad
if (isValid === true) {
// ...
}
**********************************************************************
// good
if (isValid) {
// ...
}
**********************************************************************
// bad
if (name) {
// ...
}
**********************************************************************
// good
if (name !== '') {
// ...
}
**********************************************************************
// bad
if (collection.length) {
// ...
}
**********************************************************************
// good
if (collection.length > 0) {
// ...
}
**********************************************************************
### 使用switch时,用{}包裹对case的处理。
// bad
switch (foo) {
case 1:
let x = 1;
break;
default:
class C {}
}
**********************************************************************
// good
switch (foo) {
case 1: {
let x = 1;
break;
}
case 4:
bar();
break;
default: {
class C {}
}
}
**********************************************************************
### 三元运算不嵌套,单行处理。
// bad
const foo = maybe1 > maybe2
? "bar"
: value1 > value2 ? "baz" : null;
// best
// split into 2 separated ternary expressions
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;
**********************************************************************
### 混合运算时将优先级模糊的地方使用括号括起来。
### 在块和下一条语句之间保留空白行。
// bad
const obj = {
foo() {
},
bar() {
},
};
return obj;
**********************************************************************
// good
const obj = {
foo() {
},
bar() {
},
};
return obj;
**********************************************************************
### 不要用空行填充块。
// bad
class Foo {
constructor(bar) {
this.bar = bar;
}
}
**********************************************************************
// good
function bar() {
console.log(foo);
}
**********************************************************************
### 括号内不要使用空格,括号前后空格在不同情况下有不同选择。
// bad
if ( foo ) {
console.log(foo);
}
**********************************************************************
// good
if (foo) {
console.log(foo);
}
**********************************************************************
// bad
function apple (x) {
// ...
}
**********************************************************************
// good
function apple(x) {
// ...
}
**********************************************************************
### 注意方括号中的空格,括号前后不需要空格,逗号后需要空格,逗号前避免出现空格。
// bad
const foo = [ 1, 2, 3 ];
console.log(foo[ 0 ]);
**********************************************************************
// good
const foo = [1, 2, 3];
console.log(foo[0]);
**********************************************************************
### 注意花括号中的空格,花括号需要空格,冒号前不需要空格。
// bad
const foo = {clark: 'kent'};
**********************************************************************
// good
const foo = { clark: 'kent' };
**********************************************************************
### 在尾部添加额外逗号。
### Why?这使得git diff更干净。同样,像Babel这样的编译器也将删除已编译代码中的多余尾部逗号。
### Attention!逗号不要在...rest后出现。
// bad
createHero(
firstName,
lastName,
inventorOf
);
**********************************************************************
// good
createHero(
firstName,
lastName,
inventorOf,
);
// good (note that a comma must not appear after a "rest" element)
createHero(
firstName,
lastName,
inventorOf,
...heroArgs
);
**********************************************************************
### 转换为字符串
// 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(); // isn’t guaranteed to return a string
**********************************************************************
// good
const totalScore = String(this.reviewScore);
**********************************************************************
### 转换为数字
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);
**********************************************************************
### 转为布尔值
const age = 0;
// bad
const hasAge = new Boolean(age);
// good
const hasAge = Boolean(age);
**********************************************************************
// best
const hasAge = !!age;
**********************************************************************
### 在命名对象,函数和实例时,请使用camelCase(驼峰)。
### 仅在命名构造函数或类时使用PascalCase(大驼峰)。
### 不要保存对的引用this。使用箭头函数或Function#bind。
### 箭头函数没有this,如果箭头函数里出现了this,此this一定代表外层的this。
// 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);
};
}
**********************************************************************
### 基本文件名应与默认导出文件名完全匹配。
// file 1 contents
class CheckBox {
// ...
}
export default CheckBox;
// file 2 contents
export default function fortyTwo() { return 42; }
// file 3 contents
export default function insideDirectory() {}
// in some other file
// bad
import CheckBox from './checkBox'; // PascalCase import/export, camelCase filename
import FortyTwo from './FortyTwo'; // PascalCase import/filename, camelCase export
import InsideDirectory from './InsideDirectory'; // PascalCase import/filename, camelCase export
// bad
import CheckBox from './check_box'; // PascalCase import/export, snake_case filename
import forty_two from './forty_two'; // snake_case import/filename, camelCase export
import inside_directory from './inside_directory'; // snake_case import, camelCase export
import index from './inside_directory/index'; // requiring the index file explicitly
import insideDirectory from './insideDirectory/index'; // requiring the index file explicitly
**********************************************************************
// good
import CheckBox from './CheckBox'; // PascalCase export/import/filename
import fortyTwo from './fortyTwo'; // camelCase export/import/filename
import insideDirectory from './insideDirectory'; // camelCase export/import/directory name/implicit "index"
// ^ supports both insideDirectory.js and insideDirectory/index.js
**********************************************************************
### 导出默认功能时,请使用camelCase,文件名应与函数名称相同。
function makeStyleGuide() {
// ...
}
export default makeStyleGuide;
### 在导出constructor/class/singleton/function library/bare object时使用PascalCase。
### 首字母缩写词和首字母缩写应始终全部大写或全部小写(提高可读性)。
// bad
import SmsContainer from './containers/SmsContainer';
// bad
const HttpRequests = [
// ...
];
**********************************************************************
// good
import SMSContainer from './containers/SMSContainer';
// good
const HTTPRequests = [
// ...
];
// also good
const httpRequests = [
// ...
];
**********************************************************************
// best
import TextMessageContainer from './containers/TextMessageContainer';
// best
const requests = [
// ...
];
**********************************************************************
### const在已导出,或嵌套永不更改时变量名能大写。
// 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'
};
**********************************************************************
### 使用 Number.isNaN 而不是全局的 isNaN。
### 与原有的isNaN()方法不同,不存在隐式的Number()类型转换,非NaN一律返回false。
// bad
isNaN('1.2.3'); // true
**********************************************************************
// good
Number.isNaN('1.2.3'); // false,没有隐式转换所以非NaN,返回false。
Number.isNaN(Number('1.2.3')); // true
**********************************************************************
### 使用 Number.isFinite 而不是全局的 isFinite。
### 与原有的isFinite()方法的不同之处在于,
### Number.isFinite()方法没有隐式的Number()类型转换,对于非数值一律返回false。
// bad
isFinite('2e3'); // true
**********************************************************************
// good
Number.isFinite('2e3'); // false,没有隐式转换,所以返回false。
Number.isFinite(parseInt('2e3', 10)); // true
**********************************************************************
### 使用 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);
**********************************************************************
### 让对象拥有私有成员
### 可以通过闭包完成
### 用 Promises 替代回调
// bad
require('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');
}
})
}
})
**********************************************************************
// good
require('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 更好的选择
// bad
require('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);
})
**********************************************************************
// good
import { 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()
**********************************************************************