代码复用性,提高开发效率

参考:JS_Pattern.md

  • 面向接口编程,而非面向实现编程。
  • 优先使用对象「组合 Mixin」而非类继承。

对象创建方法

  • 工厂模式创建对象,对象创建的工厂函数
  • Constructor 模式:构建实例的类的构造函数
    • ES6 的 class 语法糖:constructor, super, static, set, get
    • 稳妥构造模式:对象在内部 new Object() 构建,通过修改之后,返回对象。
    • constructor 修复
  • Prototype 原型继承模式:
    • Object.create() 基于原型的创建
  • 组合使用 ConstructorPrototype 模式
  • Builder 模式
  1. let Book = (function() {
  2. let privateVar = 1;
  3. let privateMethod = () => {};
  4. function book(name) {
  5. this.name = name;
  6. this.specialMethod = () => {};
  7. }
  8. book.prototype = {
  9. publicMethod: () => {}
  10. };
  11. return book;
  12. })();

类的继承方法

  • 类式继承Prototype Chain 利用原型让一个引用类型继承另一个引用类型的属性和方法
    • SubType.prototype = new SuperType() prototype 是一种 享元模式
    • 存在问题:无法向父类传递参数,并且会共享原型中父类的引用类型。
  • 构造函数继承
    • 存在问题:不会继承父类原型链中的方法。
function SuperClass(id) {
  this.book = [];
  this.id = id;
}
SuperClass.prototype.showBook = () => {};
function SubClass(id, name) {
  SuperClass.call(this, id);
  this.name = name;
}
  • 组合继承:将类式继承和构造函数继承的功能合并。
  • 原型继承:参考 js_patterns 中的 Prototype 模式。
  • 寄生组合式继承
    • 构造函数继承实例属性
    • 原型链共享属性和方法
let Child = Obejct.create(Parent); // 与后者等效
var inhert = (function () {
  var F = function () {};
    return function (Child, Parent) {
      F.prototype = Parent.prototype;
      Child.prototype = new F();
      Child.uper = Parent.prototype;
      Child.prototype.constructor = Child; // Constructor 修正
    }
}());
function Super(name) {
  this.name = name;
}
Super.prototype.getName = () => {};
function Sub(name, time) {
  Super.call(this, name);
  this.time = time;
}
inhert(Sub, Super);
Sub.prototype.newMethod = () => {};
  • Borrow:方法借用
    • 借用父类的构造器或者方法
      • 借用构造函数继承:子类的多重继承,ES6 语法糖 extends
        • 理解 JS 的对象类型判定的哲学:鸭式辩型 Duck Typing:In duck typing, an object’s suitability is determined by the presence of certain methods and properties (with appropriate meaning), rather than the actual type of the object.
        • 注意:不要在子类中调用父类名称
        • 注意:不要继承标准类
        • 注意:不要轻率使用猴子补丁
    • 修改原生对象原型
  • Polyfills 的兼容模式
    • 借用其他对象的构造器或者方法
  • Mixin:组合继承,使用 _.extend 类似的方法,扩展原型链

其它

  • 静态方法,挂载到 Constructor 函数上的静态方法
    • Method.xxx = () => {}
Object.assign() // 合并对象
  • 多态的实现:let args = arguments; 根据 arguments 的情形来处理一个方法的多种调用方式。

动态修改

  • Decorator 模式:装饰者模式在不修改源对象模型的情况下,向对象中注入新的方法或属性。
  • 动态选型模式,在构造函数中根据上下文属性,动态定义方法
  • 防止原型动态被修改

这一块请参考 JS设计模式 中和对象创建、类创建的相关知识。

ES6 Classes

class PersonClass extends AnimalClass {
    // equivalent of the PersonType constructor
    constructor(name, sex, job) {
        super(name, sex);
        this.job = job;
    }
    set setName(name) {
        this.name = name;
    }
    get getName() {
        return this.name;
    }

    static default() {
        this.name = 'default';
    }

    // equivalent of PersonType.prototype.sayName
    sayName() {
        console.log(this.name);
    }
}

let person = new PersonClass("Nicholas");
person.sayName();   // outputs "Nicholas"
console.log(person instanceof PersonClass);     // true
console.log(person instanceof Object);          // true
console.log(typeof PersonClass);                    // "function"
console.log(typeof PersonClass.prototype.sayName);  // "function"

// direct equivalent of PersonClass
let PersonType2 = (function() {
    "use strict";
    function PersonType2(name) {
        // make sure the function was called with new
        if ((!this instanceof PersonType2)) {
            throw new Error("Constructor must be called with new.");
        }
        this.name = name;
    }
    Object.defineProperty(PersonType2.prototype, "sayName", {
        value: function() {
            console.log(this.name);
        },
        enumerable: false,
        writable: true,
        configurable: true
    });
    return PersonType2;
}());
  1. Class declarations, unlike function declarations, are not hoisted. Class declarations act like let declarations and so exist in the temporal dead zone until execution reaches the declaration.
  2. All code inside of class declarations runs in strict mode automatically. There’s no way to opt-out of strict mode inside of classes.
  3. All methods are non-enumerable. This is a significant change from custom types, where you need to use Object.defineProperty() to make a method non-enumerable.
  4. Calling the class constructor without new throws an error.

Ref

Babel 中继承的实现

'use strict';

function _possibleConstructorReturn(self, call) {
  if (!self) {
    throw new ReferenceError("this hasn't been initialised - super() hasn't been called");
  }
  return call && (typeof call === "object" || typeof call === "function") ? call : self;
}

function _inherits(subClass, superClass) {
  // 做类型检测
  if (typeof superClass !== "function" && superClass !== null) {
    throw new TypeError("Super expression must either be null or a function, not " + typeof superClass);
  }

  // 原型链拼接,同时修正 constructor 参数
  subClass.prototype = Object.create(superClass && superClass.prototype, {
    constructor: {
      value: subClass,
      enumerable: false,
      writable: true,
      configurable: true
    }
  });

  // 原型构造器参数修正
  if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass;
}

function _classCallCheck(instance, Constructor) {
  if (!(instance instanceof Constructor)) {
    throw new TypeError("Cannot call a class as a function");
  }
}

var A = function A() {
  _classCallCheck(this, A);
};

A.job = 'title';

var B = function (_A) {
  _inherits(B, _A);

  function B(a) {
    // 继承检测
    _classCallCheck(this, B);

    // 调用父类构造器,如果有的话
    var _this = _possibleConstructorReturn(this, Object.getPrototypeOf(B).call(this, a));

    // 子类 Constructor 中的方法
    _this.darling = 'qingnan';
    return _this;
  }

  return B;
}(A);