继承

8
open
sqshada
sqshada
Posted 4 months ago

继承 #22

sqshada
sqshada
Created 4 months ago

原型链继承

父类

function SuperType () {
  this.name = 'SuperType'; // 父类属性
}
SuperType.prototype.sayName = function () { // 父类原型方法
  return this.name;
};

子类

function SubType() {
  this.subName = 'SubType'
}

SubType.prototype = new SuperType() // 重写原型对象,代之以一个新类型的实例
  // 1,新创建的对象复制了父类构造函数内的所有属性及方法
  // 2,并将原型 __proto__ 指向了父类的原型对象

SubType.prototype.saySubName = function() { // 子类原型方法
  return this.subName;
}

let instance = new SubType('SubType')

instance instanceof SubType ; // true
instance instanceof SuperType; // true

SubType instanceof SuperType; // false
SubType.prototype instanceof SuperType; // true

优点

  • 继承了父类的模板,继承了父类的原型对象

缺点

  • 新增原型属性方法必须放在子类继承之后;
  • 无法实现多继承;
  • 原型对象的所有属性被所有实例共享
  • 创建子类实例时,无法向父类构造函数传参
sqshada
sqshada
Created 4 months ago

构造继承

在子类的构造函数中调用父类的构造函数

父类

function SuperType (name) {
  this.name = name; // 父类属性
}
SuperType.prototype.sayName = function () { // 父类原型方法
  return this.name;
};

子类

function SubType () {
  // 调用 SuperType 构造函数
  SuperType.call(this, 'SuperType'); // 在子类构造函数中,向父类构造函数传参
  // 为了保证子父类的构造函数不会重写子类的属性,需要在调用父类构造函数后,定义子类的属性
  this.subName = "SubType"; // 子类属性
};
// 子类实例
let instance = new SubType(); // 运行子类构造函数,并在子类构造函数中运行父类构造函数,this绑定到子类

优点

  • 解决了子类实例共享父类引用对象的问题
  • 实现多继承可以向父类传递参数

缺点

  • 实例并不是父类的实例,只是子类的实例
  • 只能继承父类的实例属性和方法,不能继承原型属性和方法
  • 无法实现函数复用,每个子类都有父类实例函数的副本
sqshada
sqshada
Created 4 months ago

组合继承

组合继承就是将原型链继承和构造函数继承组合在一起

父类

function SuperType (name) {
  this.colors = ["red", "blue", "green"];
  this.name = name; // 父类属性
}
SuperType.prototype.sayName = function () { // 父类原型方法
  return this.name;
};

子类

function SubType (name, subName) {
  // 调用 SuperType 构造函数
  SuperType.call(this, name); // ----第二次调用 SuperType----
  this.subName = subName;
};

// ----第一次调用 SuperType----
SubType.prototype = new SuperType(); // 重写原型对象,代之以一个新类型的实例

SubType.prototype.constructor = SubType; // 组合继承需要修复构造函数指向
SubType.prototype.saySubName = function () { // 子类原型方法
  return this.subName;
}

// 子类实例
let instance = new SubType('An', 'sisterAn')
instance.colors.push('black')
console.log(instance.colors) // ["red", "blue", "green", "black"]
instance.sayName() // An
instance.saySubName() // sisterAn

let instance1 = new SubType('An1', 'sisterAn1')
console.log(instance1.colors) //  ["red", "blue", "green"]
instance1.sayName() // An1
instance1.saySubName() // sisterAn1

优点

  • 可以继承实例属性/方法,也可以继承原型属性/方法,不存在引用属性共享问题,可传参,可复用

缺点

  • 两次调用父类的构造函数,产生两份实例
sqshada
sqshada
Created 4 months ago

寄生组合继承

通过寄生方式,砍掉父类的实例属性,这样,在调用两次父类的构造的时候,就不会初始化两次实例方法/属性,避免的组合继承的缺点

父类

function SuperType (name) {
  this.colors = ["red", "blue", "green"];
  this.name = name; // 父类属性
}
SuperType.prototype.sayName = function () { // 父类原型方法
  return this.name;
};

子类

function SubType (name, subName) {
  // 调用 SuperType 构造函数
  SuperType.call(this, name); // ----第二次调用 SuperType----
  this.subName = subName;
};

// ----第一次调用 SuperType----
SubType.prototype = Object.create(SuperType.prototype); 
SubType.prototype.constructor = SubType; // 组合继承需要修复构造函数指向
SubType.prototype.saySubName = function () { // 子类原型方法
  return this.subName;
}

// 子类实例
let instance = new SubType('An', 'sisterAn')
instance.colors.push('black')
console.log(instance.colors) // ["red", "blue", "green", "black"]
instance.sayName() // An
instance.saySubName() // sisterAn

let instance1 = new SubType('An1', 'sisterAn1')
console.log(instance1.colors) //  ["red", "blue", "green"]
instance1.sayName() // An1
instance1.saySubName() // sisterAn1

优点

  • 只调用一次 SuperType 构造函数,只创建一份父类属性
  • 原型链保持不变
  • 能够正常使用 instanceof 与 isPrototypeOf
sqshada
sqshada
Created 4 months ago

Object.create

Object.create = function (obj) {
    function F() {}
    F.prototype = obj;
    return new F();
  };
sqshada
sqshada
Created 4 months ago

原型式继承

父类

function SuperType (name) {
  this.colors = ["red", "blue", "green"];
  this.name = name; // 父类属性
}
SuperType.prototype.sayName = function () { // 父类原型方法
  return this.name;
};

子类

/** 第一步 */
// 子类,通过 call 继承父类的实例属性和方法,不能继承原型属性/方法
function SubType (name, subName) {
  SuperType.call(this, name); // 调用 SuperType 的构造函数,并向其传参 
  this.subName = subName;
}

/** 第二步 */
// 解决 call 无法继承父类原型属性/方法的问题
// Object.create 方法接受传入一个作为新创建对象的原型的对象,创建一个拥有指定原型和若干个指定属性的对象
// 通过这种方法指定的任何属性都会覆盖原型对象上的同名属性
SubType.prototype = Object.create(SuperType.prototype, { 
  constructor: { // 注意指定 SubType.prototype.constructor = SubType
    value: SubType,
    enumerable: false,
    writable: true,
    configurable: true
  },
  run : { 
    value: function(){ // override
      SuperType.prototype.run.apply(this, arguments); 
      	// call super
      	// ...
    },
    enumerable: true,
    configurable: true, 
    writable: true
  }
}) 

/** 第三步 */
// 最后:解决 SubType.prototype.constructor === SuperType 的问题
// 这里,在上一步已经指定,这里不需要再操作
// SubType.prototype.constructor = SubType;

var instance = new SubType('An', 'sistenAn')

多继承混入

// 父类 SuperType
function SuperType () {}
// 父类 OtherSuperType
function OtherSuperType () {}

// 多继承子类
function AnotherType () {
    SuperType.call(this) // 继承 SuperType 的实例属性和方法
    OtherSuperType.call(this) // 继承 OtherSuperType 的实例属性和方法
}

// 继承一个类
AnotherType.prototype = Object.create(SuperType.prototype);

// 使用 Object.assign 混合其它
Object.assign(AnotherType.prototype, OtherSuperType.prototype);
// Object.assign 会把  OtherSuperType 原型上的函数拷贝到 AnotherType 原型上,使 AnotherType 的所有实例都可用 OtherSuperType 的方法

// 重新指定 constructor
AnotherType.prototype.constructor = AnotherType;

AnotherType.prototype.myMethod = function() {
     // do a thing
};

let instance = new AnotherType()
sqshada
sqshada
Created 4 months ago

ES6 继承

class People {
    constructor(name) {
        this.name = name
    }
    run() { }
}

// extends 相当于方法的继承
// 替换了上面的3行代码
class Man extends People {
    constructor(name) {
        // super 相当于属性的继承
        // 替换了 People.call(this, name)
        super(name)
        this.gender = '男'
    }
    fight() { }
}
sqshada
sqshada
Created 4 months ago

new

// 第二版的代码
function objectFactory() {
  var obj = new Object(),
    Constructor = [].shift.call(arguments);
  obj.__proto__ = Constructor.prototype;
  var ret = Constructor.apply(obj, arguments);
  return typeof ret === "object" ? ret : obj;
}
function create(func, ...args) {
  let o = Object.create(func.prototype);
  let k = func.apply(o, args);
  return typeof k === "object" ? k : o;
}