Skip to content

1.原型链继承

js
function Factory(name) {
  this.name = name
  this.hobbyMap = {
    read: true,
    write: true
  }
}

Factory.prototype.getName = function() {
  return this.name
}

function Person() {}

Person.prototype = new Factory('yxp')

var p1 = new Person()
p1.hobbyMap.read = false
console.log(p1, p1.name, p1.hobbyMap, p1.getName())

var p2 = new Person()
console.log(p2, p2.name, p2.hobbyMap, p2.getName())

缺点:

  1. 某一实例对于实例属性的引用类型的更改,其他实例也会受到影响。
  2. 子类构建实例时不能向父类传递参数。

2.构造函数继承

js
function Factory(name) {
  this.name = name
  this.hobbyMap = {
    read: true,
    write: true
  }
}

Factory.prototype.getName = function() {
  return this.name
}

function Person(name) {
  Factory.call(this, name)
}

var p1 = new Person('yxp')
p1.hobbyMap.read = false
console.log(p1, p1.name, p1.hobbyMap, p1.getName)

var p2 = new Person('Tom')
console.log(p2, p2.name, p2.hobbyMap, p2.getName)

优点:

  1. 利用 Factory.call(this, property) 将父类实例上的属性复制给了各个子类实例。即使是引用类型,由于是全新的地址,所以互不影响。
  2. 子类构建实例时能向父类传递参数。

缺点:

  1. 无法访问父类原型上的属性和方法。
  2. 每次创建子类实例,都会调用父类构造函数,消耗性能。

3.组合继承

js
function Factory(name, age) {
  this.name = name
  this.age = age
  this.hobbyMap = {
    read: true,
    write: true
  }
}

Factory.prototype.getName = function() {
  return this.name
}

function Person(name, age, job) {
  Factory.call(this, name, age)
  this.job = job
}

Person.prototype = new Factory()
// 如果不进行这一步的话,Person.prototype.contructor 会指向 Factory。
Person.prototype.constructor = Person
// 可以在 Person.prototype 上挂载自定义方法
Person.prototype.getPersonName = function() {
  return this.name
}

var p1 = new Person('yxp', 19)

console.log(p1, p1.name, p1.hobbyMap, p1.getName())

优点:

  1. 创建子类实例时 能向父类构造函数传递参数。
  2. 某一子类实例对于引用类型属性的更改,不会影响到其他子类实例。
  3. 子类实例可以访问父类构造函数原型上的属性和方法。

缺点:

  1. 子类构造函数的原型上有多余的属性。这些属性由于属性访问原则,虽然并不会被子类实例访问到(子类实例有这些属性)。
  2. 每次创建子类实例,都会调用父类构造函数,消耗性能。

4.原型式继承

原型式继承指的是将原型指向新的对象

JS 中能实现这种继承的有两种方式:Object.create()Object.setPrototypeOf()

js
// 模拟实现一个 Object.create()
Object.createFn = function(obj) {
  function F() {}
  F.prototype = obj
  return new F()
}

原型式继承的优缺点与原型链继承一致。

另外这里备注下 Object.create()Object.setPrototypeOf 的实际 polyfill

js
// Object.create() 的 polyfill
if (typeof Object.create !== "function") {
  Object.create = function (proto, propertiesObject) {
    if (typeof proto !== 'object' && typeof proto !== 'function') {
      throw new TypeError('Object prototype may only be an Object: ' + proto);
    } else if (proto === null) {
      throw new Error("This browser's implementation of Object.create is a shim and doesn't support 'null' as the first argument.");
    }

    if (typeof propertiesObject !== 'undefined') throw new Error("This browser's implementation of Object.create is a shim and doesn't support a second argument.");

    function F() {}
    F.prototype = proto;

    return new F();
  };
}

// Object.setPrototypeOf() 的 polyfill
if (!Object.setPrototypeOf) {
  // 仅适用于Chrome和FireFox,在IE中不工作:
  Object.prototype.setPrototypeOf = function (obj, proto) {
    if (obj.__proto__) {
      obj.__proto__ = proto;
      return obj;
    } else {
      // 如果你想返回 prototype of Object.create(null):
      var Fn = function () {
        for (var key in obj) {
          Object.defineProperty(this, key, {
            value: obj[key],
          });
        }
      };
      Fn.prototype = proto;
      return new Fn();
    }
  }
}

TIP

__proto__ 已逐渐被废弃。 [[prototype]] 也是不支持实例直接访问的。所以更好的方式是利用 constructorprototype 属性。

5.寄生式继承

在原型式继承的基础上添加自定义方法

js
Object.createFn = function(obj) {
  function F() {}
  F.prototype = obj
  return new F()
}
function createAnother(obj) {
  const o = Object.createFn(obj)
  o.getAnother = function(value) {
    return value
  }
  return o
}

var value = createAnother({})
console.log(value)

寄生式继承的优缺点与原型链继承保持一致。

6.寄生组合继承

组合式继承的缺点在于二次调用了构造函数,现在利用寄生式继承,改进下组合式继承。

解决办法是将子类构造函数的原型指向父类构造函数原型的一个子拷贝。

js
function Factory(name, age) {
  this.name = name
  this.age = age
  this.hobbyMap = {
    read: true,
    write: true
  }
}
Factory.prototype.getName = function() {
  return this.name
}

function Person(name, age, job) {
  Factory.call(this, name, age)
  this.job = job
}
Person.prototype = Object.create(Factory.prototype)
Person.prototype.constructor = Person

var p1 = new Person('yxp', 19)
var p2 = new Person('Tom', 27)
console.log(p1)
console.log(p2)

7.Es6中的extends继承

原始代码:

js
class Factory {
  constructor(name, age) {
    this.name = name
    this.age = age
    this.hobbyMap = {
      read: true,
      write: true
    }
  }
  getName() {
    return this.name
  }
  static say() {
    alert('hello')
  }
}
Factory.play = function() {
  alert('play')
}
class Person extends Factory {
  constructor(name, age, job) {
    super(name)
    this.age = age
    this.job = job
  }
  getJob() {
    return this.job
  }
}
var p = new Person('yxp', 19, 'programmer')

经过 babel 转译后的代码:

js
// 继承 子类 父类
function _inherits(subClass, superClass) {
  if (typeof superClass !== "function" && superClass !== null) {
    throw new TypeError("Super expression must either be null or a function");
  }
  /*
  proto
    - 新创建对象的原型对象。
  propertiesObject
    - 可选。需要传入一个对象,该对象的属性类型参照Object.defineProperties()的第二个参数。如果该参数被指定且不为 undefined,该传入对象的自有可枚举属性(即其自身定义的属性,而不是其原型链上的枚举属性)将为新创建的对象添加指定的属性值和对应的属性描述符。
  */ 
  // 原型属性的继承  
  subClass.prototype = Object.create(superClass && superClass.prototype, {
    constructor: {
      value: subClass,
      writable: true,
      configurable: true
    }
  });
  // 静态属性的继承
  if (superClass) _setPrototypeOf(subClass, superClass);
}

// 创建class 入参是构造函数 原型属性 静态属性
function _createClass(Constructor, protoProps, staticProps) {
  if (protoProps) _defineProperties(Constructor.prototype, protoProps);
  if (staticProps) _defineProperties(Constructor, staticProps);
  return Constructor;
}
// Object.defineProperty
function _defineProperties(target, props) {
  for (var i = 0; i < props.length; i++) {
    var descriptor = props[i];
    descriptor.enumerable = descriptor.enumerable || false;
    descriptor.configurable = true;
    if ("value" in descriptor) descriptor.writable = true;
    Object.defineProperty(target, descriptor.key, descriptor);
  } 
}

function _setPrototypeOf(o, p) {
  _setPrototypeOf = Object.setPrototypeOf || function _setPrototypeOf(o, p) {
    o.__proto__ = p;
    return o;
  };
  return _setPrototypeOf(o, p);
}

function _getPrototypeOf(o) {
  _getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf : function _getPrototypeOf(o) {
    return o.__proto__ || Object.getPrototypeOf(o);
  };
  return _getPrototypeOf(o);
}

function _createSuper(Derived) {
  // 判断是否能利用Reflect创建construct
  var hasNativeReflectConstruct = _isNativeReflectConstruct();
  return function _createSuperInternal() {
    var Super = _getPrototypeOf(Derived), result;
    // this => 子类 因为_createSuperInternal这个方法会由子类调用
    if (!hasNativeReflectConstruct) {
      // result 会是 子类实例
      var NewTarget = _getPrototypeOf(this).constructor;
      result = Reflect.construct(Super, arguments, NewTarget);
    } else {
      // result 会是 undefined
      result = Super.apply(this, arguments);
    }
    return _possibleConstructorReturn(this, result);
  };
}

function _isNativeReflectConstruct() {
  if (typeof Reflect === "undefined" || !Reflect.construct) return false;
  if (Reflect.construct.sham) return false;
  if (typeof Proxy === "function") return true;
  try {
    Boolean.prototype.valueOf.call(Reflect.construct(Boolean, [], function () {}));
    return true;
  } catch (e) {
    return false;
  }
}
// 检测class是否由new运算符调用
function _classCallCheck(instance, Constructor) {
  if (!(instance instanceof Constructor)) {
    throw new TypeError("Cannot call a class as a function");
  }
}

function _possibleConstructorReturn(self, call) {
  if (call && (_typeof(call) === "object" || typeof call === "function")) {
    // 如果call是子类实例的话 就返回call
    return call;
  } else if (call !== void 0) {
    // 如果call不是子类实例 也不是 undefined 的话,抛错
    throw new TypeError("Derived constructors may only return object or undefined");
  }
  // 如果call是undefined的话 返回self self其实也是子类实例
  return _assertThisInitialized(self);
}

function _assertThisInitialized(self) {
  if (self === void 0) {
    throw new ReferenceError("this hasn't been initialised - super() hasn't been called");
  }
  return self;
}

function _typeof(obj) {
  "@babel/helpers - typeof";
  if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") {
    _typeof = function _typeof(obj) {
      return typeof obj;
    };
  } else {
    _typeof = function _typeof(obj) {
      return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj;
    };
  }
  return _typeof(obj);
}

var Factory = function () {
  function Factory(name, age) {
    _classCallCheck(this, Factory);
    this.name = name;
    this.age = age;
    this.hobbyMap = {
      read: true,
      write: true
    };
  }
  _createClass(Factory, [{
    key: "getName",
    value: function getName() {
      return this.name;
    }
  }], [{
    key: "say",
    value: function say() {
      alert('hello');
    }
  }]);

  return Factory;
}();

Factory.play = function () {
  alert('play');
};

var Person = function (_Factory) {
  // 子类继承父类上的原型属性和静态属性
  _inherits(Person, _Factory);

  var _super = _createSuper(Person);

  function Person(name, age, job) {
    var _this2;

    _classCallCheck(this, Person);

    _this2 = _super.call(this, name);
    _this2.age = age;
    _this2.job = job;
    return _this2;
  }

  _createClass(Person, [{
    key: "getJob",
    value: function getJob() {
      return this.job;
    }
  }]);

  return Person;
}(Factory);

var p = new Person('yxp', 19, 'programmer')