当前位置: 首页 > 工具软件 > Jedi-js > 使用案例 >

JavaScript Clean-Code

凌蕴藉
2023-12-01

Airbnb JavaScript Style Guide()

clean-code-javascript


上面两篇文章的整合,建议去看原文,此笔记自留。

Array - 数组

### 使用...来复制数值

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

 

Destructuring - 解构

### 在访问和使用对象的多个属性时,请使用对象分解,这样可以减少临时引用。

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

 

String - 字符串

### 使用模板字符串,且避免使用多余空格。

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

 

Functions - 函数

### 使用命名函数表达式而不是函数声明
### 函数声明会提升,容易在定义函数前被调用-不利于可读性。(有利有弊)

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

 

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);
**********************************************************************
  
### 避免将箭头函数语避免将箭头函数语法(=>)与比较运算符(<=,>=)混淆。

// bad
const itemHeight = (item) => item.height >= 256 ? item.largeSize : item.smallSize;
**********************************************************************
// good
const itemHeight = (item) => (item.height <= 256 ? item.largeSize : item.smallSize);
**********************************************************************

 

Classes & Constructors - 类和构造函数

### 始终使用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();
**********************************************************************

 

Modules - 模块

### 不要使用通配符导入
### 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() {}
**********************************************************************

 

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

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

### 现在不要使用生成器。

Properties - 属性

### 访问属性时使用。

### 使用[]访问变量属性。

### 计算指数时使用指数运算符 ** 。

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

 

Variables - 变量

### 使用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.'
}
**********************************************************************

 

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 - 比较运算符和等号

### 空数组空对象都是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;
**********************************************************************
  
### 混合运算时将优先级模糊的地方使用括号括起来。

 

WhiteSpace - 空白符

### 在块和下一条语句之间保留空白行。


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

 

Comma - 逗号

### 在尾部添加额外逗号。
### 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
);
**********************************************************************

 

Type Casting & Coercion - 类型转换

### 转换为字符串

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

 

Naming Conventions - 命名规范

### 在命名对象,函数和实例时,请使用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'
};
**********************************************************************

Standard Library - 标准库

### 使用 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
**********************************************************************

 

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 替代回调

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

 

 类似资料: