JavaScript:原型继承实现

75 阅读5分钟

构造函数

function Person(name) {
    this.name = name; // 实例基本属性 (该属性,强调私有,不共享)
    this.cars = [1]; // 实例引用属性 (该属性,强调私用,不共享)
    this.say = function() { // 实例引用属性 (该属性,强调复用,需要共享)
        console.log('hello')
    }
}
//注意:数组和方法都属于‘实例引用属性’,但是数组强调私有、不共享的。
//     方法需要复用、共享。
//注意:在构造函数中,一般很少有数组形式的引用属性
//     大部分情况都是:基本属性 + 方法。

原型对象

实例__proto__,构造函数 prototype 指向原型对象

原型对象为所有实例储存共享的数据或者方法。原型对象只有一个实例可以有多个且属性和方法都是相互独立不影响的。

为了属性(实例基本属性)的私有性、以及方法(实例引用属性)的复用、共享。个人建议:

  • 将属性封装在构造函数中
  • 将方法定义在原型对象上
function Person(name) {
    this.name=name
    this.cars = []
}
Person.prototype.say = function () {
    console.log('我是', this.name)
}
// 不建议这么写,会覆盖原型链,导致原型缺少 constructor 属性
// 而且__proto__ 是指向原型的内存地址
// 直接改下如果位置不当会导致之前实例获取共享的数据或者方法
Person.prototype ={
   say:function () {
    console.log('我是', this.name)
   }
}

继承 本示例都继承于Person

继承为了让代码的复用性更高,扩展性更强

  1. 原型继承
-核心:将父类实例作为子类原型
-优点:方法复用
    -由于方法定义在父类的原型上,复用了父类构造函数的方法。比如say方法。
-缺点:
    -创建子类实例的时候,不能传参数。
    -子类实例共享了父类构造函数的引用属性,比如cars属性。
function WoMan() {
  this.sex='nv'
}
WoMan.prototype = new Person()
//测试
let woman1 = new WoMan()
let woman2 = new WoMan()
woman1.say()
woman2.say()
woman1.say === woman2.say  // true
//
woman1.name  // undefind
woman2.name  // undefind
woman1.cars.push('BMW')
woman2.cars  // BMW
  1. 构造函数继承
-核心:借用父类的构造函数来增强子类实例,等于是复制父类的实例属性给子类。
-优点:实例之间独立。
    -创建子类实例,可以向父类构造函数传参数。
    -子类实例不共享父类构造函数的引用属性。如arr属性
-缺点:
    -父类的方法不能复用
由于方法在父构造函数中定义,导致方法不能复用(因为每次创建子类实例都要创建一遍方法)。比如say方法。(方法应该要复用、共享)
    -子类实例,继承不了父类原型上的属性。(因为没有用到原型)
// 构造函数
// 无法使用原型上的方法
function whitePerson() {
    // 没有 new 只能用到 父类的 属性和方法 不能使用原型的方法
    Person.call(this)
} 
  1. 组合继承
-核心:通过调用父类构造函数,继承父类的属性并保留传参的优点;然后通过将父类实例作为子类原型,实现函数复用。
-优点:
  -保留构造函数的优点:创建子类实例,可以向父类构造函数传参数。
    -保留原型链的优点:父类的实例方法定义在父类的原型对象上,可以实现方法复用。
    -不共享父类的引用属性。比如arr属性

-缺点:
  -由于调用了2次父类的构造方法,会存在一份多余的父类实例属性。
  -注意:'组合继承'这种方式,要记得修复Child.prototype.constructor指向
function africaPerson() {
    Person.call(this)
}
// 过度使用,有两个父类实例
africaPerson.prototype = new Person()
  1. 寄生继承
// - Object.create() 的内部原理:
// 其中,o 是新创建对象的原型(对象)
function objectCreate(o) {
    function F() {}
    F.prototype = o
    return new F()
}
function inheritPrototype(subType, superType) {
    // 隔离原型
    let prototype = object.create(superType.prototype); // 创建对象
    prototype.constructor = subType; // 增强对象
    subType.prototype = prototype; // 赋值对象
}
function chinaPerson(){}
inheritPrototype(chinaPerson,Person)
/*这里只调用了一次 SuperType 构造函数,
避免了 SubType.prototype 上不必要也用不到的属性【添加到子类构造函数原型对象上的父类构造函数属性】,
因此可以说这个例子的效率更高。而且,原型链仍然保持不变,因此 instanceof 操作符和 isPrototypeOf()
方法正常有效。寄生式组合继承可以算是引用类型继承的最佳模式。*/
  1. 关键字继承(后续我会详细讲解下)
// ES6
class Person{
	constructor(){}
    say(){}
}
class WoMan extends  Person{
  constructor(){
  super()
  }
}

//  babel 转换来的 ES5
"use strict";

function _typeof(obj) {
  "@babel/helpers - typeof";
  return (
    (_typeof =
      "function" == typeof Symbol && "symbol" == typeof Symbol.iterator
        ? function (obj) {
            return typeof obj;
          }
        : function (obj) {
            return obj &&
              "function" == typeof Symbol &&
              obj.constructor === Symbol &&
              obj !== Symbol.prototype
              ? "symbol"
              : typeof obj;
          }),
    _typeof(obj)
  );
}
function _inherits(subClass, superClass) {
  if (typeof superClass !== "function" && superClass !== null) {
    throw new TypeError("Super expression must either be null or a function");
  }
  subClass.prototype = Object.create(superClass && superClass.prototype, {
    constructor: { value: subClass, writable: true, configurable: true }
  });
  Object.defineProperty(subClass, "prototype", { writable: false });
  if (superClass) _setPrototypeOf(subClass, superClass);
}
function _setPrototypeOf(o, p) {
  _setPrototypeOf = Object.setPrototypeOf
    ? Object.setPrototypeOf.bind()
    : function _setPrototypeOf(o, p) {
        o.__proto__ = p;
        return o;
      };
  return _setPrototypeOf(o, p);
}
function _createSuper(Derived) {
  var hasNativeReflectConstruct = _isNativeReflectConstruct();
  return function _createSuperInternal() {
    var Super = _getPrototypeOf(Derived),
      result;
    if (hasNativeReflectConstruct) {
      var NewTarget = _getPrototypeOf(this).constructor;
      result = Reflect.construct(Super, arguments, NewTarget);
    } else {
      result = Super.apply(this, arguments);
    }
    return _possibleConstructorReturn(this, result);
  };
}
function _possibleConstructorReturn(self, call) {
  if (call && (_typeof(call) === "object" || typeof call === "function")) {
    return call;
  } else if (call !== void 0) {
    throw new TypeError(
      "Derived constructors may only return object or undefined"
    );
  }
  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 _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;
  }
}
function _getPrototypeOf(o) {
  _getPrototypeOf = Object.setPrototypeOf
    ? Object.getPrototypeOf.bind()
    : function _getPrototypeOf(o) {
        return o.__proto__ || Object.getPrototypeOf(o);
      };
  return _getPrototypeOf(o);
}
function _classCallCheck(instance, Constructor) {
  if (!(instance instanceof Constructor)) {
    throw new TypeError("Cannot call a class as a function");
  }
}
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 _createClass(Constructor, protoProps, staticProps) {
  if (protoProps) _defineProperties(Constructor.prototype, protoProps);
  if (staticProps) _defineProperties(Constructor, staticProps);
  Object.defineProperty(Constructor, "prototype", { writable: false });
  return Constructor;
}
var Person = /*#__PURE__*/ (function () {
  function Person() {
    _classCallCheck(this, Person);
  }
  _createClass(Person, [
    {
      key: "say",
      value: function say() {}
    }
  ]);
  return Person;
})();
var WoMan = /*#__PURE__*/ (function (_Person) {
  _inherits(WoMan, _Person);
  var _super = _createSuper(WoMan);
  function WoMan() {
    _classCallCheck(this, WoMan);
    return _super.call(this);
  }
  return _createClass(WoMan);
})(Person);