博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
JavaScript 代码整洁之道
阅读量:6834 次
发布时间:2019-06-26

本文共 7671 字,大约阅读时间需要 25 分钟。

整洁的代码不仅仅是让人看起来舒服,更重要的是遵循一些规范能够让你的代码更容易维护,同时降低bug几率。

1. 用命名的变量代替数组下标

// badconst address = "One Infinite Loop, Cupertino 95014";const cityZipCodeRegex = /^[^,\\]+[,\\\s]+(.+?)\s*(\d{5})?$/;saveCityZipCode(  // 下标1,2不易于理解  address.match(cityZipCodeRegex)[1],  address.match(cityZipCodeRegex)[2]);
// goodconst address = "One Infinite Loop, Cupertino 95014";const cityZipCodeRegex = /^[^,\\]+[,\\\s]+(.+?)\s*(\d{5})?$/;// 使用数组解构更好的命名变量const [, city, zipCode] = address.match(cityZipCodeRegex) || [];saveCityZipCode(city, zipCode);

2. 函数的参数最好<=2个,尽量避免3个。

如果有很多参数就利用object传递,并使用解构。

3. 一个函数只做一件事。

好处在于compose, test, and reason about。

4. 不要自行扩展原型

如果想扩展原型,可以先继承再添加方法,防止污染。

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

5. 用多态来代替条件语句

// badif (type === 'text') {    // do something} else if (type === 'select') {    // do something else}

个人写这种代码的一种常用方式是:

const control = {    text: {        mapper() {},        restore(){},        name: 'this is a text field',    },    select: {        mapper() {},        restore(){},        name: 'this is a select field',    }}control[type].mapper();

实际上就是多态(polymorphism),也可以考虑用class的方式,大概这样:

class Field {    ...}class TextField extends Field {    mapper(){}    restore(){}    name = 'this is a text field';}class SelectField extends Field {    mapper(){}    restore(){}    name = 'this i```s a select field';}
  1. 使用getter和setter函数。
// badfunction makeBankAccount() {  // ...  return {    balance: 0    // ...  };}const account = makeBankAccount();account.balance = 100;
// goodfunction makeBankAccount() {  // this one is private  let balance = 0;  // a "getter", made public via the returned object below  function getBalance() {    return balance;  }  // a "setter", made public via the returned object below  function setBalance(amount) {    // ... validate before updating the balance    balance = amount;  }  return {    // ...    getBalance,    setBalance  };}const account = makeBankAccount();account.setBalance(100);

你可以在getter和setter里面做很多事情而不需要修改每一个.balance的地方。

7. Prefer composition over inheritance

尽量用组合来代替继承,什么情况下用继承:

Your inheritance represents an "is-a" relationship and not a "has-a" relationship (Human->Animal vs. User->UserDetails).

You can reuse code from the base classes (Humans can move like all animals).
You want to make global changes to derived classes by changing a base class. (Change the caloric expenditure of all animals when they move).

8. SOLID

Single Responsibility Principle 单一职责原则

There should never be more than one reason for a class to change,一个类被改变的原因数量应该尽可能降低。如果一个类中功能太多,当你修改其中一点时会无法估量任何引用该类的模块所受到的影响。

Open/Closed Principle 开放封闭原则

用户可以在不修改内部实现的前提下自行扩展功能。例如有一个Http模块,内部会根据环境判断用哪个adaptor。如果用户要添加adaptor就必须修改Http模块。

// badclass 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 => {        // transform response and return      });    } else if (this.adapter.name === "nodeAdapter") {      return makeHttpCall(url).then(response => {        // transform response and return      });    }  }}function makeAjaxCall(url) {  // request and return promise}function makeHttpCall(url) {  // request and return promise}
// goodclass AjaxAdapter extends Adapter {  constructor() {    super();    this.name = "ajaxAdapter";  }  request(url) {    // request and return promise  }}class NodeAdapter extends Adapter {  constructor() {    super();    this.name = "nodeAdapter";  }  request(url) {    // request and return promise  }}class HttpRequester {  constructor(adapter) {    this.adapter = adapter;  }  fetch(url) {    return this.adapter.request(url).then(response => {      // transform response and return    });  }}

Liskov Substitution Principle 里式替换原则

父类和子类应该可以被交换使用而不会出错。

// 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 {  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);    const area = rectangle.getArea(); // BAD: Returns 25 for Square. Should be 20.    rectangle.render(area);  });}const rectangles = [new Rectangle(), new Rectangle(), new Square()];renderLargeRectangles(rectangles);

上面的Rectangle不能直接替换Square,因为会导致计算面积错误,考虑将计算面积的方法抽象出来:

class Shape {  setColor(color) {    // ...  }  render(area) {    // ...  }}class Rectangle extends Shape {  constructor(width, height) {    super();    this.width = width;    this.height = height;  }  getArea() {    return this.width * this.height;  }}class Square extends Shape {  constructor(length) {    super();    this.length = length;  }  getArea() {    return this.length * this.length;  }}function renderLargeShapes(shapes) {  shapes.forEach(shape => {    const area = shape.getArea();    shape.render(area);  });}const shapes = [new Rectangle(4, 5), new Rectangle(4, 5), new Square(5)];renderLargeShapes(shapes);

Interface Segregation Principle 接口隔离原则

Clients should not be forced to depend upon interfaces that they do not use。举例来说,一个功能模块需要设计必须传的参数和可选参数,不应该强迫用户使用可选参数。

Dependency Inversion Principle 依赖注入原则

// badclass InventoryRequester {  constructor() {    this.REQ_METHODS = ["HTTP"];  }  requestItem(item) {    // ...  }}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);    });  }}const inventoryTracker = new InventoryTracker(["apples", "bananas"]);inventoryTracker.requestItems();

上面例子在于,InventoryTracker内部实例化了InventoryRequester,也就意味着high-level的模块需要知道low-level模块的细节(比如实例化InventoryRequester需要知道它的构造参数等,或者说需要import该模块,造成耦合)。

// 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.const inventoryTracker = new InventoryTracker(  ["apples", "bananas"],  new InventoryRequesterV2());inventoryTracker.requestItems();

直接传入low-level的实例而不需要考虑它是如何被实例化的,high-level只需要依赖抽象的接口就可以完成对子模块的调用。

9. 注释

Comments are an apology, not a requirement. Good code mostly documents itself. 好的代码是自解释的。

你会经常地遇到 bug 和其它一些问题。这可能会让人沮丧,但你要尽量保持冷静,并系统地去思考。记住实践是解决问题的最佳方法。

我们采集的是石头,但是必须时刻展望未来的大教堂。

转载于:https://blog.51cto.com/14227711/2371524

你可能感兴趣的文章
写给大学时代的我们——英语很重要
查看>>
Access模糊查询
查看>>
redmine安装部署文档
查看>>
Exchange 2010做OWA的https重定向时会话延迟
查看>>
Spring与Quartz实现定期任务
查看>>
设计模式之建造者模式实例
查看>>
我的友情链接
查看>>
我的友情链接
查看>>
用Java写算法之五:快速排序
查看>>
UK 更新惊魂记
查看>>
ZooKeeper-3.3.4集群安装配置
查看>>
《Spring In Action》第三版中文版 Chapter 1 Piece 3
查看>>
用图片拼接图片 C#
查看>>
python对多个数据库执行mysql的source命令
查看>>
我的友情链接
查看>>
JS 实现连续滚动的思路
查看>>
关于cvs merge的问题的研究
查看>>
解决chrome崩溃的问题
查看>>
判断系统是不是 XP
查看>>
设计模式详解(总纲)
查看>>