代码规范整理

变量、常量

  • 驼峰(使用有意义且可发音)命名变量、函数名、方法名
  • 大写命名常量(单词间用“_”)
  • 使用解释变量(占位符)【_】
  • 使用默认参数代替短路或条件

函数、方法

  • 使用对象做函数参数(理性情况参数个数为2个或更少)
  • forEach前先过滤,不在forEach中做判断(函数应该只做一件事)
  • 函数应该只有一个抽象
  • 删除重复代码
  • 使用Object.assign设置默认对象
  • 不要将标示(用作判断的东西)用作函数参数
  • 不写入全局函数
  • 支持函数式编程而不是命令式编程
  • 封装条件语句
  • 避免否定条件句
  • 避免条件句(使用多态性来实现相同的任务)
  • 避免类型检查(typeof、instanceof)(可借助typescript)
  • 不要过度优化
  • 删除死代码

对象和数据结构

  • 使用getters和setters
  • 使对象具有私有成员(可以通过闭包实现、private)

类(Class)

  • 与ES5普通功能相比,推荐使用ES2015/ES6级别
  • 使用方法链接(return this)
  • 选择组合而不是继承
  • 单一责任原则(SRP)
    正如Clean代码中所述,“一个类的更改原因不应该超过一个”。将一个类塞满大量功能是很诱人,例如当你在你的航班上只能带一个手提箱。这样做的问题是,你的类在概念上没有凝聚力,它会给它很多改变的理由。尽可能减少更改类的次数非常重要。这一点很重要,因为如果一个类中有太多的功能,而您修改了其中的一部分,那么很难理解这将如何影响代码库中的其他依赖模块。

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()) {
      // ...
    }
  }
}
  • 开/关原理(OCP)
    正如bertrandmeyer所说,“软件实体(类、模块、函数等)应该是开放的,但是对于修改是关闭的。”这意味着什么呢?这个原则基本上是说,您应该允许用户在不改变现有代码的情况下添加新功能。
    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 => {
        // 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
}

Good:

class 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替代原理(LSP)
    这是一个非常简单的概念的可怕术语。它的正式定义是“如果s是T的一个子类型,那么T类型的对象可以被s类型的对象替换(即,s类型的对象可以替换T类型的对象),而不会改变该程序的任何期望属性(正确性、执行的任务等)”,这是一个更可怕的定义。
    最好的解释是,如果有父类和子类,那么基类和子类可以互换使用,而不会得到错误的结果。这可能仍然令人困惑,所以让我们看看经典的方形矩形示例。从数学上讲,正方形是一个矩形,但如果通过继承使用“is-a”关系对其进行建模,则很快就会遇到麻烦。
    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 {
  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);

Good:

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);
  • 接口隔离原理
    JavaScript没有接口,所以这个原则并不像其他原则那样严格适用。然而,即使在JavaScript缺少类型系统的情况下,它也非常重要和相关。
    ISP声明“不应该强迫客户端依赖于他们不使用的接口。”接口是JavaScript中的隐式契约,因为duck类型。
    在JavaScript中演示这一原理的一个很好的例子是针对需要大型设置对象的类。不要求客户端设置大量的选项是有益的,因为大多数时候他们不需要所有的设置。将它们设为可选有助于防止出现“胖接口”。
    Bad:
class DOMTraverser {
  constructor(settings) {
    this.settings = settings;
    this.setup();
  }

  setup() {
    this.rootNode = this.settings.rootNode;
    this.settings.animationModule.setup();
  }

  traverse() {
    // ...
  }
}

const $ = new DOMTraverser({
  rootNode: document.getElementsByTagName("body"),
  animationModule() {} // 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() {
    // ...
  }
}

const $ = new DOMTraverser({
  rootNode: document.getElementsByTagName("body"),
  options: {
    animationModule() {}
  }
});
  • 依赖倒置原理(DIP)
    这一原则阐述了两个基本事项:
    1、高级模块不应依赖于低级模块。两者都应该依赖于抽象。
    2、抽象不应依赖细节。细节应该依靠抽象。
    这一点一开始可能很难理解,但如果您使用过AngularJS,您就会看到依赖注入(DI)形式的这一原则的实现。虽然它们不是完全相同的概念,但是DIP阻止高级模块了解其低级模块的细节并设置它们。它可以通过DI实现这一点。这样做的一个巨大好处是减少了模块之间的耦合。耦合是一种非常糟糕的开发模式,因为它使代码很难重构。
    如前所述,JavaScript没有接口,因此依赖的抽象是隐式契约。也就是说,一个对象/类向另一个对象/类公开的方法和属性。在下面的示例中,隐式约定是InventoryTracker的任何请求模块都将具有一个requestItems方法。
    Bad:
class 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();

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

测试

  • 每个测试的单一概念

并发

  • 使用Promises,而不是callbacks(回调)
  • 使用Async/Await比使用Promises代码更干净

错误处理

  • 不要忽略捕捉到的错误
  • 不要忽视被拒绝(失败)的Promises(请求)

格式化

  • 使用一致的大写字母
  • 函数调用方和被调用方应接近(保持函数调用方在被调用方的上面)

注释

  • 只注释具有业务逻辑复杂性的内容
  • 不要在代码库中留下注释掉的代码
  • 不需要版本更新、日志注释
  • 避免使用位置标记
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值