Class的继承

473 阅读5分钟
  1. Es6中class 可以看做是一个语法糖
  2. 新的class写法只是让对象原型的写法更加清晰,更加面向对象编程。
  3. Class通过exends关键字进行继承
class Father{
    
}
class Son extends Father{

}
  1. 代码 定义了一个Son,通过exends继承Father类的所有属性和方法,由于没有部署任何代码,所以这两个类完全一样,等于完全复制了一个Father类
class Son  extends Father{
    constructor(name,age,city){
        super(name,age)
        this.city=city
    }
      toString () { 
                  return this.city+ " " +super.toString();//调用父类的toString()
            }
}
  1. super 关键字,类的构造函数,用来新建父类的this对象。 子类必须的constructor方法中调用super方法,否则新建的实例时会报错,如果不调用 super方法,自雷就得不到this对象;
  2. ES5的继承,实质上是线创造自雷的实例对象this,然后再将父类的方法添加到this上,(parent.apply(this)),ES6的继承机制完全不同,实质上是先创造对象this,所以必须先调用super犯法,然后再用子类的构造函数修改this;如果子类没有定义cinstructor方法,这个方法会默认添加,也就是说不管有没有显示定义任何一个子类都有constructor方法。
class Son extends Father{}
//等于
class Son extends Parent{
    constructor(...args){
        super(...args)
    }
}
  1. 在子类的构造函数中,只有调用super之后,才能使用this关键字。 因为子类实例的构件,是基于对父类实例的加工,之后super方法才能返回父类实例;
class Father{
    constructor(x,y){
        this.x=x;
        this.y=y;
    }
}
class Son extends Father{
 constructor(x,y,color){
  //必须先调用super
     super(x,y)
     this.color=color;
     console.log(this.color,x,y)
 }
}
let s =new Son(25,8,'green')
  1. Object.getPrototypeOf()方法用来从子类获取父类
Object.getPrototypeOf(Son)===Father
//用来判断是否继承
  1. super关键字 即可当做函数使用也可以当做对象使用。 ·super当做函数调用时,代表父类的构造函数,Es6要求,子类的构造函数必须执行一个super函数。
class Father { }

class Son extends Father {
       constructor () {
                   super();
              }
}

super肃然代表了父类Father的构造函数,但是返回的是子类son的实例,既super内部的this只想的是son,因此,super()在这里相当于 Father.constructor.call(this) 而且作为函数时,super()智能在子类的构造函数中。

class A{
    constructor(){
        console.log(new.target.name)
    }
}

class B extends A{
    constructor(){
        super()
    }
}
new A();
new B();
//new.target是指当前正在执行的函数,在super()执行时,他指向的是子类B的构造函数,二部是父类A的构造函数,super()内部的this指向的是B

· super 作为对象时,在普通方法中,指向的的是父类的原型对象,在静态方法中,指向的是父类。

class Father{
    getName(){
        return '123'
    }
}

class Son extends Father{
    constructor{
        super();
        consloe.log(super.getName())
    }
}
let s=new Son();

子类son中的super.getName()就是将super当做一个对象使用,这时,super在普通放方法中,指向Father.prototype,所以super.getName()就相当于Father.prototype.getName(),由于super指向父类的圆形对象,所以定义在父类实例上的方法或者属性是无法通过super调用的。

class Father {
      constructor () {
              this.p  =2
          }
}

class Son extends Father {
          get  m ( ) {
                   return super.p;
          }
          getValue ( ) {
                    return super.a;
           }
}
let  s = new Son();
s.m //undefined

p是父类Father的实例属性,super.p就引用不到它 如果实行定义在父类原型对象上,super就可以取到。

class A {}
A.prototype.x = 4;

class B extends A {
  constructor() {
    super();
    console.log(super.x) // 4
  }
}

let b = new B()

属性X是定义在A.prototype上面所以super.x可以取到他的值,Es6规定,通过super调用父类的方法时,super会绑定子类的this

class  Father {
      constructor () {
           this.x =1;//这个this指向的是Father对象的实例
      }
      print () {
           console.log(this.x);
      }
}

class Son extends Father {
      constructor () {
             super();
               this.x = 2;//这个this指向的是Son对象的实例
      }
          m() {
           super.print();       
          }
}
let s = new  Son();
s.m();
//2 

super.print()虽然调用的是Father.prototype.print(),但是Father.prototype.print()会绑定子类son的this,导致输入2而不是1 ,实际上执行super.print.call(this) 如果super作为对象,用在静态方法中,这时super将指向父类,而不是父类的原型对象。

class Parent {
    //静态方法
           static myMethod (msg) {
                      console.log("static",msg);
               }
               //普通方法
           myMethod (msg) {
                    console.log("instance" ,msg);
               }
}

class Child  extends Parent {
        //静态方法
          static myMethod(msg) {
               super.myMethod(msg);
          }
           //普通方法
            myMethod (msg) {
           super.myMethod(msg);
           }
 }

Child.myMethod(1);
//static 1
var child = new Child();
child.myMethod(2);
//instance 2

super 在静态方法中指向父类,在普通方法中指向父类原型对象。 使用super的时候,必须显式指定是作为函数、还是作为对象使用。

类的prototype属性和proto属性

  1. 大多数浏览器的es5实现之中,每一个对象都有Proto属性,指向相对应的构造函数的prototype属性。 class 作为构造函数的语法糖,同时有prototype属性和proto属性,同事存在两条继承链。
    • 子类proto属性,标识构造函数的继承,总是指着父类
    • 子类prototype属性的proto属性,表示方法的继承,总是指向父类的prototype属性。
class A{

}
class B{

}
//Object.prototype.__proto__
// Object.setPrototypeOf()方法的实现:

// Object.setPrototypeOf =  function (obj, proto) {
// obj.__proto__ = proto;
//   return obj ;
// }
//Object.setPrototypeOf( B.prototype ,  A.prototype );
//等同于
//B.prototype.__proto__ =  A.prototype ;

//Object.setPrototypeOf(B, A);
//等同于
//B.__proto__ = A;


//B的实例继承A的实例
Object.setPrototypeOf(B.prototype,A.prototype);
//B的实例继承A的静态属性。
Object.setPrototypeOf(B,A)
const b=new B();

这两条继承链可以理解为:作为一个对象,子类型B原型(proto属性),是父类(A),作为一个构造函数,子类B的圆形对象(prototype属性)是父类的原型对象(prototype)的实例。

extends的继承目标

extends的关键字后面可以跟很多类型的值:

class B extends A{
}

只要A有一个prototype属性的函数,就能被B继承,由于函数prototype属性(除了Function.prototype函数),因此A可以使任意函数。

  1. 子类继承Object类
class A extends Object {
}
A.__proto__ === Object //true;
A.prototype.__proto__ === Object.prototype //true
A就是构造函数Object的复制,A的实例就是Object的实例。

  1. 不存在任何继承
 class A{}
 A.__proto__ === Function.prototype //true
A.prototype.__proto__ = Object.prototype //true

A作为一个基类,不存在任何继承,就是一个普通函数,所以直接继承Function.prototype。但是A调用后返回一个空对象(既Object实例),所以A.prototype.proto指向构造函数(Object)的prototype属性。 #####实例的proto属性 子类实例的proto属性的proto属性,指向父类实例的proto属性,也就是说子类的原型的原型就是父类的原型。

原生构造函数的继承

Boolean()
Number()
String()
Array()
Date()
Function()
RegExp()
Error()
Object()

extends关键字不仅可以用来继承类,还可以用来继承原生的构造函数。因此可以在原生数据结构的基础上,定义自己的数据结构。