js之继承

一、构造函数、原型和实例的关系

  每一个构造函数都有一个原型对象——constructor.prototype

  原型对象包含一个指向构造函数的指针。constructor.prototype.constructor = constructor

  实例包含一个指向原型对象的内部指针。instance.__proto__ === constructor.prototype

  

  实例可以调用原型上的方法,也可以通过原型上的constructor属性来知道自己是被哪个构造函数所创建的。

 

二、原型链继承

  即将子类的原型对象设置为超类的一个实例。为什么不直接设置为超类的原型对象?

  这是因为如果需要修改子类的原型(添加新的方法或属性),就会影响超类的原型。而采用超类的实例作为子类的原型,修改子类的原型即修改超类的一个实例,不会影响超类的原型本身。

  

function Super(name) {
  this.name = name;      
}
// 可以在超类原型上添加一些公用方法或属性
Super.prototype.getName = function() {
  console.log(this.name);  
}

function Sub(name) {
  this.name = name;        
}

// 继承Super,Sub的实例会按照 子类实例、子类原型(超类的一个实例)、超类原型的顺序查找属性或方法。当在某个位置查找到时就停止查找。
Sub.prototype = new Super();
// 修改原型上的constructor属性 Sub.prototype.constructor = Sub; let instance
= new Sub()

  注意:将超类的实例作为子类的原型,则修改超类实例上的引用类型数据,子类实例访问时都会受到影响。

 

三、构造函数继承

  在子类构造函数内部调用超类构造函数。

  

function Super(age) {
  this.age = age
}

function Sub(name, age) {
  this.name = name;
  // 调用超类构造函数代码初始化对象
  Super.call(this, age)
}

  问题:超类的原型定义的方法,对于子类实例不可用;

 

四、组合继承

  将原型链继承和借用构造函数继承组合到一起

  用原型链继承实现对超类原型上方法和属性的继承,借用构造函数实现对超类实例的继承。

function Super(age) {
  this.age = age;
}

Super.prototype.sayName = function() {
  console.log(this.name);  
}

function Sub(name, age) {
  this.name = name;
  Super.call(this, age)  
}

Sub.prototype = new Super()

Sub.prototype.constructor = Sub;

  问题:会两次调用超类构造函数。子类实例会包含超类实例的所有实例属性,需要重写覆盖

五、原型式继承

  ECMAScript 5 通过新增Object.create()方法规范了原型式继承

function object(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;
}
//设置临时的构造函数原型为超类原型,返回一个临时构造函数的实例作为子类的原型,将原型的constructor设置为子类构造函数

function Super(age) {
  this.age = age;
  this.colors = ['red', 'blue']
}

Super.prototype.sayName = function() {
  console.log(this.name);
}

function Sub(name, age) {
  this.name = name;
  Super.call(this, age)
}

inheritPrototype(Sub, Super)

Sub.prototype.sayAge = function() {
  console.log(this.age)
}

  目前来说,寄生组合式继承为开发者常用的继承方式

七、es6的class继承

  class可以通过extends关键字实现继承。

class Super {
  constructor(age) {
    this.age = age;
  }  
}

class Sub extends Super {
  constructor(name, age) {
    this.name = name; // 报错
    super(age);
    this.name = name;
 }
}

  子类必须在constructor方法中调用super方法,否则新建实例时会报错。这是因为子类自己的this对象,必须先通过超类的构造函数来完成塑造,得到父类的实例属性和方法。然后才可以在this上加上自己的实例属性和方法。(如果不调用super方法,子类就得不到this对象);

  es5的继承是先创造子类的实例对象this,再将父类构造函数的this修改为子类实例进行初始化。es6是先将父类实例对象的属性和方法加到this上,然后再用子类的构造函数对this上增加属性(这里可以理解为子类必须在constructor方法中调用super,利用父类的constructor来创建子类实例,在父类构造函数中对子类实例进行增加属性后,再由子类构造函数对其增加属性)

  es6通过super实现实例属性的继承,另外还有原型方法属性的继承

  父类的静态方法,会被子类继承。因为静态方法其实是定义在构造函数这个对象上的方法。需要注意的是,父类静态方法中的this是指向父类,子类所继承的静态方法中的this指向的是子类。

  7.1 super关键字

    super关键字,可以在constructor中当作函数使用,代表父类的构造函数。然后在其他普通方法中只能作为对象,指向父类的原型对象;在静态方法中,指向父类;

    super调用父类的方法时,方法内部的this指向当前子类实例。

  

  7.2 类的prototype属性和__proto__属性

  每个实例都有__proto__属性,指向对应的构造函数的prototype属性,即可以通过__proto__访问定义在原型对象上的方法和属性;

  class作为构造函数的语法糖,同时具有prototype属性和__proto__属性,因此同时存在两条继承链。

  子类的__proto__ 属性, 表示构造函数的继承,总是指向父类

  子类的prototype属性的__proto__属性,表示方法的继承,总是指向父类的prototype

   可以理解为:作为一个对象,子类的原型(__proto__)指向父类;作为一个构造函数,子类的原型对象(prototype)是父类的原型对象的实例。

   7.3 实例的__proto__属性

  子类实例的__proto__属性指向子类的原型对象,而子类的原型对象其实是父类的一个实例,所以有子类实例的原型的原型等于超类实例的原型。

 

class Super {}

class Sub extends Super {}

let super = new Super();

let sub = new Sub();

sub.__proto__.__proto__ === super.__proto__

 

 

 

 

六、确定原型与实例的关系

  6.1 instance instanceof Object;左边为实例,右边为构造函数

    确定原则为依次判断instance的原型是否出现在有右边构造函数的原型中

  6.2 constructor.prototype.isPrototypeOf(instance)

    Super.isPrototypeOf(Sub); // 返回false

    只要原型链中出现过的原型,isPrototypeOf()方法就会返回true

  6.3 Object.getPrototypeOf()

  返回子类所继承的父类,或者实例的原型

  Object.getPrototypeOf(Sub); // 返回Super的代码

  Object.getPrototypeOf(sub);  // 返回原型对象

class A {
}

class B extends A {
}

B.__proto__ === A // true
B.prototype.__proto__ === A.prototype // true

  继承的模式

class A {
}

class B {
}

// B 的实例继承 A 的实例
Object.setPrototypeOf(B.prototype, A.prototype);

// B 继承 A 的静态属性
Object.setPrototypeOf(B, A);

const b = new B();

 

  

 

posted @ 2019-09-06 09:32  iszhangjin  阅读(212)  评论(0)    收藏  举报