js笔记 |整洁代码技巧

笔记源于:https://gitee.com/mirrors_trending/clean-code-javascript?_from=gitee_search

变量

1 使用可读的名称

Bad

const yyyymmdstr = moment().format("YYYY/MM/DD");

Good

const currentDate = moment().format("YYYY/MM/DD");

2 使用同个单词表示同个变量

Bad

getUserInfo();
getClientData();
getCustomerRecord();

Good

getUser();

3 使用可理解的名称

Bad

setTimeout(blastOff, 86400000);// 不知道86400000是什么意思

Good

//通过大写的常量来声明
const MILLISECONDS_IN_A_DAY = 60 * 60 * 24 * 1000;//86400000;

setTimeout(blastOff, MILLISECONDS_IN_A_DAY);

4 使用具有解释性的名称

Bad

const address = "One Infinite Loop, Cupertino 95014";
const cityZipCodeRegex = /^[^,\\]+[,\\\s]+(.+?)\s*(\d{5})?$/;
saveCityZipCode(
  address.match(cityZipCodeRegex)[1],
  address.match(cityZipCodeRegex)[2]
);

Good

const address = "One Infinite Loop, Cupertino 95014";
const cityZipCodeRegex = /^[^,\\]+[,\\\s]+(.+?)\s*(\d{5})?$/;
const [_, city, zipCode] = address.match(cityZipCodeRegex) || [];
saveCityZipCode(city, zipCode);

5 使用明确的映射

Bad

const locations = ["Austin", "New York", "San Francisco"];
locations.forEach(l => {
  doStuff();
  doSomeOtherStuff();
  // ...
  // ...
  // ...
  // Wait, what is `l` for again?
  dispatch(l);
});

Good

const locations = ["Austin", "New York", "San Francisco"];
locations.forEach(location => {
  doStuff();
  doSomeOtherStuff();
  // ...
  // ...
  // ...
  dispatch(location);
});

6 不加没有意义的语境

如果你的class或者对象名称已经告诉你一些东西,你就不必在变量中重复

Bad

const Car = {
  carMake: "Honda",
  carModel: "Accord",
  carColor: "Blue"
};

function paintCar(car) {
  car.carColor = "Red";
}

Good

const Car = {
  make: "Honda",
  model: "Accord",
  color: "Blue"
};

function paintCar(car) {
  car.color = "Red";
}

7 使用默认arguments而非条件判断

Bad

function createMicrobrewery(name) {
  const breweryName = name || "Hipster Brew Co.";
  // ...
}

Good

function createMicrobrewery(name = "Hipster Brew Co.") {
  // ...
}

函数

1 使用少量的实参

超过三个会导致组合爆炸,需要测试多次。两个是最理想的,任何超过三个的应该被合并。为了使函数具有明显的属性,可使用ES2015 / ES6解构语法

Bad

function createMenu(title, body, buttonText, cancellable) {
  // ...
}

createMenu("Foo", "Bar", "Baz", true);

Good

function createMenu({ title, body, buttonText, cancellable }) {
  // ...
}

createMenu({
  title: "Foo",
  body: "Bar",
  buttonText: "Baz",
  cancellable: true
});

2 函数只做一件事

当一个函数做很多事的时候,会变得组合、测试和理解
Bad

function emailClients(clients) {
  clients.forEach(client => {
    const clientRecord = database.lookup(client);
    if (clientRecord.isActive()) {
      email(client);
    }
  });
}

Good

function emailActiveClients(clients) {
  clients.filter(isActiveClient).forEach(email);
}

function isActiveClient(client) {
  const clientRecord = database.lookup(client);
  return clientRecord.isActive();
}

3 函数名应该表明它们是做什么的

Bad

function addToDate(date, month) {
  // ...
}
const date = new Date();
addToDate(date, 1);//很难判断是什么增加了

Good

function addMonthToDate(month, date) {
  // ...
}
const date = new Date();
addMonthToDate(1, date);

4 删除重复代码

有时候会因为小的差别而重新写一遍代码,这会导致很多重复代码。

Bad

function showDeveloperList(developers) {
  developers.forEach(developer => {
    const expectedSalary = developer.calculateExpectedSalary();
    const experience = developer.getExperience();
    const githubLink = developer.getGithubLink();
    const data = {
      expectedSalary,
      experience,
      githubLink
    };
    render(data);
  });
}

function showManagerList(managers) {
  managers.forEach(manager => {
    const expectedSalary = manager.calculateExpectedSalary();
    const experience = manager.getExperience();
    const portfolio = manager.getMBAProjects();
    const data = {
      expectedSalary,
      experience,
      portfolio
    };

    render(data);
  });
}

Good

function showEmployeeList(employees) {
  employees.forEach(employee => {
    const expectedSalary = employee.calculateExpectedSalary();
    const experience = employee.getExperience();
    const data = {
      expectedSalary,
      experience
    };

    switch (employee.type) {
      case "manager":
        data.portfolio = employee.getMBAProjects();
        break;
      case "developer":
        data.githubLink = employee.getGithubLink();
        break;
    }
    render(data);
  });
}

5 使用Object.assign来设置默认对象

Bad

const 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

const menuConfig = {
  title: "Order",
  buttonText: "Send",
  cancellable: true
};

function createMenu(config) {
  let finalConfig = Object.assign(
    {
      title: "Foo",
      body: "Bar",
      buttonText: "Baz",
      cancellable: true
    },
    config
  );
  return finalConfig
}

createMenu(menuConfig);

6 不要使用flag让函数完成多件事

Bad

function createFile(name, temp) {
  if (temp) {
    fs.create(`./temp/${name}`);
  } else {
    fs.create(name);
  }
}

Good

function createFile(name) {
  fs.create(name);
}

function createTempFile(name) {
  createFile(`./temp/${name}`);
}

7 避免副作用

  • 如果修改一些全局变量,容易产生副作用。要避免在没有结构的对象之间分享状态、或是使用可在任何地方写入数据的类型。

    Bad

    let name = "Ryan McDermott";
    function splitIntoFirstAndLastName() {
      name = name.split(" ");
    }
    splitIntoFirstAndLastName();
    console.log(name); // ['Ryan', 'McDermott'];
    

Good
```js
function createFile(name) {
fs.create(name);
}

function createTempFile(name) {
  createFile(`./temp/${name}`);
}
```
  • 为了避免一些误触导致对象或者数组进行修改,可对其进行编辑并返回克隆,让原数据不会受到影响。

    Bad

    const addItemToCart = (cart, item) => {
      cart.push({ item, date: Date.now() });
    };
    

Good
js const addItemToCart = (cart, item) => { return [...cart, { item, date: Date.now() }]; };

8 不要写全局函数

使用全局函数是不好的做法,因为很可能和其他库冲突。比如你想写一个显示两数组差异的diff方法,你可能会写在Array.prototype上,那它可能会和另一个执行相同操作的库发生冲突。用ES2015/ES6类并将数组扩展为全局会更好

Bad

Array.prototype.diff = function diff(comparisonArray) {
  const hash = new Set(comparisonArray);
  return this.filter(elem => !hash.has(elem));
};

Good

class SuperArray extends Array {
  diff(comparisonArray) {
    const hash = new Set(comparisonArray);
    return this.filter(elem => !hash.has(elem));
  }
}

9 封装条件

Bad

if (fsm.state === "fetching" && isEmpty(listNode)) {
  // ...
}

Good

function shouldShowSpinner(fsm, listNode) {
  return fsm.state === "fetching" && isEmpty(listNode);
}

if (shouldShowSpinner(fsmInstance, listNodeInstance)) {
  // ...
}

10 避免否定条件

Bad

function isDOMNodeNotPresent(node) {
  // ...
}

if (!isDOMNodeNotPresent(node)) {
  // ...
}

Good

function isDOMNodePresent(node) {
  // ...
}

if (isDOMNodePresent(node)) {
  // ...
}

11 避免条件语句

Bad

class Airplane {
  // ...
  getCruisingAltitude() {
    switch (this.type) {
      case "777":
        return this.getMaxAltitude() - this.getPassengerCount();
      case "Air Force One":
        return this.getMaxAltitude();
      case "Cessna":
        return this.getMaxAltitude() - this.getFuelExpenditure();
    }
  }
}

Good

class Airplane {
  // ...
}

class Boeing777 extends Airplane {
  // ...
  getCruisingAltitude() {
    return this.getMaxAltitude() - this.getPassengerCount();
  }
}

class AirForceOne extends Airplane {
  // ...
  getCruisingAltitude() {
    return this.getMaxAltitude();
  }
}

class Cessna extends Airplane {
  // ...
  getCruisingAltitude() {
    return this.getMaxAltitude() - this.getFuelExpenditure();
  }
}

12 避免判断数据类型

  • 使用一致的方法

    Bad

    function travelToTexas(vehicle) {
      if (vehicle instanceof Bicycle) {
        vehicle.pedal(this.currentLocation, new Location("texas"));
      } else if (vehicle instanceof Car) {
        vehicle.drive(this.currentLocation, new Location("texas"));
      }
    }
    

    Good

    function travelToTexas(vehicle) {
      vehicle.move(this.currentLocation, new Location("texas"));
    }
    
  • 使用TypeScript

    Bad

    function combine(val1, val2) {
      if (
        (typeof val1 === "number" && typeof val2 === "number") ||
        (typeof val1 === "string" && typeof val2 === "string")
      ) {
        return val1 + val2;
      }
    
      throw new Error("Must be of type String or Number");
    }
    

    Good

    function combine(val1, val2) {
      return val1 + val2;
    }
    

对象与类

1 使用getters和setters

这种方法可能会比直接在对象中查找属性要好,下面是该方法的一些优点:

  • 除了获得对象属性之外,如果还想做更多的事,就不需要查找和更改代码库中的每个访问器。
  • 通过set是验证变得更简单
  • 封装内部表示

Bad

function makeBankAccount() {
  return {
    balance: 0
  };
}

const account = makeBankAccount();
account.balance = 100;

Good

function makeBankAccount() {
  let balance = 0;// this one is private
	
  //getter
  function getBalance() {
    return balance;
  }
	
  //setter
  function setBalance(amount) {
    // ... 在更新数据前进行验证
    balance = amount;
  }

  return {
    getBalance,
    setBalance
  };
}

const account = makeBankAccount();
account.setBalance(100);

2 使用链式方法

Bad

class Car {
  constructor(make, model, color) {
    this.make = make;
    this.model = model;
    this.color = color;
  }

  setMake(make) {
    this.make = make;
  }

  setModel(model) {
    this.model = model;
  }

  setColor(color) {
    this.color = color;
  }

  save() {
    console.log(this.make, this.model, this.color);
  }
}

const car = new Car("Ford", "F-150", "red");
car.setColor("pink");
car.save();

Good

class Car {
  constructor(make, model, color) {
    this.make = make;
    this.model = model;
    this.color = color;
  }

  setMake(make) {
    this.make = make;
    return this;
  }

  setModel(model) {
    this.model = model;
    return this;
  }

  setColor(color) {
    this.color = color;
    return this;
  }

  save() {
    console.log(this.make, this.model, this.color);
    return this;
  }
}

const car = new Car("Ford", "F-150", "red").setColor("pink").save();

3 优先考虑组合而非继承

如果是下面的情况,使用继承会更有意义,否则使用组合即可:

  • 你的继承表示“是”关系,而不是“具有”关系
  • 需要复用基类中的代码
  • 你想通过更改基类对派生类进行全局更改

Bad

class Employee {
  constructor(name, email) {
    this.name = name;
    this.email = email;
  }
}

// 不好,因为员工“拥有”税收数据,而EmployeeTaxData不是员工的类型
class EmployeeTaxData extends Employee {
  constructor(ssn, salary) {
    super();
    this.ssn = ssn;
    this.salary = salary;
  }
}

Good

class EmployeeTaxData {
  constructor(ssn, salary) {
    this.ssn = ssn;
    this.salary = salary;
  }
}

class Employee {
  constructor(name, email) {
    this.name = name;
    this.email = email;
  }

  setTaxData(ssn, salary) {
    this.taxData = new EmployeeTaxData(ssn, salary);
  }
  // ...
}

其他

1 单一责任原则

Bad

class UserSettings {
  constructor(user) {
    this.user = user;
  }

  changeSettings(settings) {
    if (this.verifyCredentials()) {
      // ...
    }
  }

  verifyCredentials() {
    // ...
  }
}

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()) {
      // ...
    }
  }
}

2 开放封闭原则

应该在不更改现有代码的情况下添加新功能

Bad

class AjaxAdapter extends Adapter {
  constructor() {
    super();
    this.name = "ajaxAdapter";
  }
}

class NodeAdapter extends Adapter {
  constructor() {
    super();
    this.name = "nodeAdapter";
  }
}

class HttpRequester {
  constructor(adapter) {
    this.adapter = adapter;
  }

  fetch(url) {
    if (this.adapter.name === "ajaxAdapter") {
      return makeAjaxCall(url).then(response => {
        // 转化响应并返回
      });
    } else if (this.adapter.name === "nodeAdapter") {
      return makeHttpCall(url).then(response => {
        // 转化响应并返回
      });
    }
  }
}

function makeAjaxCall(url) {
  // 请求并返回promise
}

function makeHttpCall(url) {
  // 请求并返回promise
}

Good

class AjaxAdapter extends Adapter {
  constructor() {
    super();
    this.name = "ajaxAdapter";
  }

  request(url) {
    // 请求并返回promise
  }
}

class NodeAdapter extends Adapter {
  constructor() {
    super();
    this.name = "nodeAdapter";
  }

  request(url) {
    // 请求并返回promise
  }
}

class HttpRequester {
  constructor(adapter) {
    this.adapter = adapter;
  }

  fetch(url) {
    return this.adapter.request(url).then(response => {
      // 转化响应并返回
    });
  }
}
posted @ 2021-05-01 10:46  sanhuamao  阅读(125)  评论(0编辑  收藏  举报