大家好,我是小庄。

明天来梳理一下ES6中的继承。

顺带记录一下其中比拟容易忘记的知识点。

1、extends关键字

实际上,继承的要害是extends:

class myClass{}class children extends myClass{}

剖析:
下面通过extends 继承了myClass的所有属性和办法。

2、super关键字

super关键字在类中有两种齐全不同的表现形式:

1、代表函数时应用

ES6 要求,子类的构造函数必须执行一次super函数。代表父类的构造函数。作为函数时,super()只能用在子类的构造函数之中,用在其余中央就会报错。

class A {}class B extends A {  constructor() {    super();  }}

下面中super尽管是示意A的构造函数,然而返回的是子类B的实例。即super外部的this指的是B的实例


下面一行话用代码来解释就是:

A.prototype.constructor.call(this)。

2、代表对象时应用

super作为对象时,在一般办法中,指向父类的原型对象;在静态方法中,指向父类。


尽管集体感觉这种设计无疑会减少程序员对于super的了解难度,然而相熟就好了。
要辨别什么是原型对象和对象自身。

class A {  constructor() {    this.a = 2;  }  p() {    return 2;  }}class B extends A {  constructor() {    super();    console.log(super.p()); // 2  }  get m() {    return super.a; // undefined  }}let b = new B();b.m;

**super.p()很显著是作为对象应用的,这里就代指父类的原型对象,而p办法是定义在A的原型下面的,所以返回值是2。天经地义调用b.m就取不到值。


这也从侧面阐明:constructor()办法个别状况下就是只A自身,而类中的个别办法是定义在类的原型对象上的。**


来看另外一个例子:

class A {  constructor() {    this.x = 1;  }  print() {    console.log(this.x);  }}class B extends A {  constructor() {    super();    this.x = 2;  }  m() {    super.print();  }}let b = new B();b.m() 

猜一猜最初会输入什么,1或者2?


由下面的一些内容推出,调用print办法,而print办法是定义在A类中的,那么打印的值是不是应该为1呢?

答案是2.
这个JS函数的执行作用域无关。

该怎么了解呢?

当函数执行的时候会生成一个作用域,运行到b.m()时,会创立属于m的一个上下文,m外面的this指向他自身,print被调用时,引擎会先在print外部找this.x,没找到就去运行的上一级找,就找到了m,m中也没有就去m定义的上一级找,如果还没找到就一级一级的向上查找,发现constructor中有一个变量x,就终止持续查找。

如果略微改变一下下面B的代码:

class B extends A {  constructor() {    super();  }  m() {    super.print();  }}let b = new B();b.m() 

此时,答案就是1.


因为B继承了A,B的原型是指向A的,在B中找不到就会去A中找,所以答案是1。



为了增强记忆再看一个实例:

class A {  constructor() {    this.x = 1;  }}class B extends A {  constructor() {    super();    this.x = 2;    super.x = 3;    console.log(super.x); // undefined    console.log(this.x); // 3  }}let b = new B();

请问一个打印值是多少?

有同学很自信的说那不就是 1嘛。

答案是错的。

当在子类B中调用super(); 还记得代表什么吗?

A.prototype.constructor.call(this)。

是的,super.x = 3;其实指的是子类B中x的赋值,相当于 this.x=3。

而在输入super.x时,执行的是A.prototype.x。而A的原型上并没有x所以会输入undefined。

尽管这看起来是有点绕,但还是解释的通。
显然第二次输入的值就是3.

补充助于了解的代码:

class A {    constructor() {      this.x = 1;    }    test() {        console.log('这边是测试');    }    y = 2}  class B extends A {    constructor() {        super();        this.x = 2;        // super.x = 3, 其实指的是子类B中x的赋值,相当于 this.x=3        super.x = 3;        // super.test = function testFun() {}, 相当于 this.test = function testFun(){}        super.test = function testFun() {            console.log('执行了这边的测试');        }        // super.testB = function() {}, 相当于 this.testB = function(){}        super.testB = function() {            console.log('这里是批改后的testB函数');        }        console.log(super.test());// '这边是测试'        console.log(super.y);// undefined        console.log(super.x); // undefined        console.log(this.x); // 3    }    testB() {        console.log('这里是testB函数');    }}let b = new B();b.test();   // '执行了这边的测试'b.testB(); // '这里是批改后的testB函数'

3、类的 prototype 属性和__proto__属性

又到了绕弯的环节,还是那句话相熟就好了。尽管我感觉这种设计的确不是那么的正当。

类作为构造函数,那么具备prototype 属性也天经地义。那么__proto__属性是哪里来的?

JS万物皆对象,尽管不精确然而类的确也是对象的一种,所以__proto__就来了,有人把__proto__叫做隐式原型,其实不妥。proto__作为JS原型链的桥梁,只是这种属性在不同的浏览器中裸露的水平不同,Google就能够拜访对象的__proto。
对于原型链的问题留作当前在独自解释,回到类上来,类同时具备prototype 属性和__proto__属性,那么类的原型链是这样的:

class A {}class B extends A {}B.__proto__ === A // trueB.prototype.__proto__ === A.prototype // true

你大略会从中看出prototype和__proto__的区别。

这两条继承链,能够这样了解:作为一个对象,子类(B)的原型(__proto__属性)是父类(A);作为一个构造函数,子类(B)的原型对象(prototype属性)是父类的原型对象(prototype属性)的实例。

由此能够看出:

子类的原型__proto__是父类,那么子类的原型的原型就是父类的原型。

4、补充

关注公众号:【深漂程序员小庄】,内含丰盛的学习资源和面试教训(不限前端、java、算法),还有学习交换群可加,并且还有各大厂大佬可一起交流学习,一起提高~增加小庄微信,回复【加群】,可退出互联网技术交换群。

本文由mdnice多平台公布