赞
踩
今天来聊聊es6类的继承,上一次关于类的一些写法和特性已经总结的差不多了,所以这次重点就在继承上面了。
抛开ES5那种写法,只谈ES6。
实际上,继承的关键是extends:
class myClass{
}
class children extends myClass{
}
分析:
上面通过extends 继承了myClass的所有属性和方法,事实上,children 类和myClass类是一模一样的。
super关键字在类中有两种完全不同的表现形式:
ES6 要求,子类的构造函数必须执行一次super函数。代表父类的构造函数。作为函数时,super()只能用在子类的构造函数之中,用在其他地方就会报错。
class A {}
class B extends A {
constructor() {
super();
}
}
上面中super虽然是表示A的构造函数,然而返回的是子类B的实例。即super内部的this指的是B的实例。
上面一行话用代码来解释就是:
A.prototype.constructor.call(this)。
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; //
}
}
let b = new B();
b.m // undefined
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);
console.log(this.x);
}
}
let b = new B();
请问一个打印值是多少?
有同学很自信的说那不就是 1嘛,
no,no,no, too young too native.
当在子类B中调用super(); 还记得代表什么吗?
A.prototype.constructor.call(this)。
是的 super.x = 3;其实指的是子类B 中x的赋值,相当于 this.x=3。而在输出super.x时,执行的是A.prototype.x。而A的原型上并没有x所以会输出undefined。
虽然这看起来是有点绕,但还是解释的通。
显然第二次输出的值就是3.
又到了绕弯的环节,还是那句话熟悉就好了。虽然我觉得这种设计确实不是那么的合理。
类作为构造函数,那么具有prototype 属性也理所当然。那么__proto__属性是哪里来的?
JS万物皆对象,虽然不准确但是类确实也是对象的一种,所以__proto__就来了,有人把__proto__叫做隐式原型,其实不妥。proto__作为JS原型链的桥梁,只是这种属性在不同的浏览器中暴露的程度不同,Google就可以访问对象的__proto。
关于原型链的问题留作以后在单独解释,回到类上来,类同时具有prototype 属性和__proto__属性,那么类的原型链是这样的:
class A {
}
class B extends A {
}
B.__proto__ === A // true
B.prototype.__proto__ === A.prototype // true
你大概会从中看出prototype和__proto__的区别。
这两条继承链,可以这样理解:作为一个对象,子类(B)的原型(__proto__属性)是父类(A);作为一个构造函数,子类(B)的原型对象(prototype属性)是父类的原型对象(prototype属性)的实例。
由此可以看出:
子类的原型__proto__是父类,那么子类的原型的原型就是父类的原型。
返回再看一遍
差不多了
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。