单一职责原则

修改一个类的理由不应该超过一个,最小化对一个类需要修改的次数是非常必要。
一个类的功能不应该太复杂,如果过于复杂应该拆分成多个类

  1. // ❌
  2. class UserSettings {
  3. constructor(user) {
  4. this.user = user;
  5. }
  6. changeSettings(settings) {
  7. if (this.verifyCredentials) {
  8. // ...
  9. }
  10. }
  11. verifyCredentials(user) {
  12. // ...
  13. }
  14. }
  15. // ✔️ 拆分为多个类,做到更大意义上的解耦
  16. class UserAuth {
  17. constructor(user) {
  18. this.user = user;
  19. }
  20. verifyCredentials() {
  21. // ...
  22. }
  23. }
  24. class UserSettings {
  25. constructor(user) {
  26. this.user = user;
  27. this.auth = new UserAuth(user);
  28. }
  29. changeSettings(settings) {
  30. if (this.auth.verifyCredentials) {
  31. // ...
  32. }
  33. }
  34. }

开/闭原则

    • 允许用户方便地扩展代码模块功能
    • 不是打开JS文件源码手动对其修改,代码应该是封闭
      1. class AjaxRequester {
      2. constructor() {
      3. this.HTTP_METHODS = ['POST', 'PUT', 'GET'];
      4. }
      5. get(url) {
      6. // ...
      7. }
      8. addHTTPMethod(method) { // 提供一个函数方法供外界的扩展其http请求方法
      9. this.HTTP_METHODS.push(method);
      10. }
      11. }

      利斯科夫替代原则

      子类对象应该能替换其超类对象被使用:

      即有一个父类和子类,当采用子类替换父类时不应该产生错误

接口隔离原则

客户端不应该依赖不需要的接口,一个类对别一个类的依赖应该在建立在最小的接口上。

在JS中,当一个类需要许多参数才能生成一个对象时,或大多数时候不需要设置这么多参数,此时减少对配置参数数量的需要是有意义的

  1. // ❌
  2. /*
  3. 定义一个DOM遍历类
  4. 构造函数中传入一个settings对象,并调用setup来初始化rootNode和animationModule
  5. 所以实例化这个类时就需要转入rootNode和animationModule
  6. */
  7. class DOMTraverser {
  8. constructor(settings) {
  9. this.settings = settings;
  10. this.setup();
  11. }
  12. setup(settings) {
  13. this.rootNode = this.settings.rootNode;
  14. this.animationModule.setup();
  15. }
  16. traverse() {
  17. // ...
  18. }
  19. }
  20. let $ = new DOMTraverser({
  21. rootNode: document.getElementsByTagName('body'),
  22. animationModule: function() {
  23. // ...
  24. }
  25. });
  26. // ✔️ 但经发现animationModule这个模块不是每次遍历都都需要,所以改为给一个options选项,
  27. /*
  28. 但经发现animationModule这个模块不是每次遍历都都需要。
  29. 所以改为给一个options选项,将不需要用到的属性放到options里
  30. 通过判断是否传入这个属性来进行相应的初始化
  31. */
  32. class DOMTraverser {
  33. constructor(settings) {
  34. this.settings = settings;
  35. this.options = settings.options;
  36. this.setup();
  37. }
  38. setup(settings) {
  39. this.rootNode = this.settings.rootNode;
  40. this.setupOptions();
  41. }
  42. setupOptions() {
  43. if (this.options.animationModule) {
  44. // ...
  45. }
  46. }
  47. traverse() {
  48. // ...
  49. }
  50. }
  51. let $ = new DOMTraverser({
  52. rootNode: document.getElementsByTagName('body'),
  53. options: {
  54. animationModule: function() {}
  55. }
  56. });

继承使用Class而不是Function

ES5的方式在继承构造和方法定义方面可读性较差,在需要继承时优先选用ES6 class。
虽然本质是一样的,但对于读写的人来说这种方式最为简单。
语雀内容