基本用法
Class之间可以通过extends关键字实现继承,这比ES5的通过修改原型链实现继承,要清晰和方便很多
class Test extends Component {}
上面代码定义了一个Test类,该类通过extends关键字,继承了Component类的所有属性和方法。但是由于没有部署任何代码,所以这两个类完全一样,等于复制了一个Component类。下面,我们在Test内部加上代码。
class Test extends Component {
constructor(props) {
super(props); // 调用父类constructor(props)
}
toTest() {
super.toTest(); // 调用父类toTest()
}
}
上面代码中,constructor方法和toTest方法之中,都出现了super关键字,它在这里表示父类的构造函数,用来新建父类的this对象。
子类必须在constructor方法中调用super方法,否则新建实例时会报错。这是因为子类没有自己的this对象,而是继承父类的this对象,然后对其进行加工。如果不调用super方法,子类就得不到this对象
class Component {}
class Test extends Component {
cosntructor() {
}
}
let a = new Test();
上面代码中,Test继承了父类Component,但是它的构造函数没有调用super方法,导致新建实例时报错。
ES5的继承,实质是先创造子类的实例对象this,然后再将父类的方法添加到this上面(Parent.apply(this))。ES6的继承机制完全不同,实质是先创造父类的实例对象this(所以必须先调用super方法),然后再用子类的构造函数修改this。
如果子类没有定义constructor方法,这个方法会被默认添加,代码如下。也就是说,不管有没有显式定义,任何一个子类都有constructor方法。
constructor(...args) {
super(...args);
}
另一个需要注意的地方是,在子类的构造函数中,只有调用super之后,才可以使用this关键字,否则会报错。这是因为子类实例的构建,是基于对父类实例加工,只有super方法才能返回父类实例。
class Component {
constructor(props) {
this.props = props;
}
}
class Test extends Component {
cosntructor(props, a) {
this.a = a; // ReferenceError
super(props);
this.a = a; // 正确
}
}
上面代码中,子类的constructor方法没有调用super之前,就使用this关键字,结果报错,而放在super方法之后就是正确的。
类的prototype属性和proto属性
大多数浏览器的ES5实现之中,每一个对象都有__proto__属性,指向对应的构造函数的prototype属性。Class作为构造函数的语法糖,同时有prototype属性和__proto__属性,因此同时存在两条继承链。
- 子类的
__proto__属性,表示构造函数的继承,总是指向父类 - 子类
prototype属性的__proto__属性,表示方法的继承,总是指向父类的prototype属性
class A { }
class B extends A { }
B.__proto__ === A; // true
B.prototype.__proto__ === A.prototype; // true
上面代码中,子类B的__proto__属性指向父类A,子类B的prototype属性的__proto__属性指向父类A的prototype属性。
Extends的继承目标
extends关键字后面可以跟多种类型的值
class B extends A {}
上面代码的A,只要是一个有prototype属性的函数,就能被B继承。由于函数都有prototype属性(除了Function.prototype函数),因此A可以是任意函数。
下面,讨论三种特殊情况。
第一种特殊情况,子类继承Object类。
class A extends Object {
}
A.__proto__ === Object; // true
A.prototype.__proto__ === Object.prototype; // true
这种情况下,A其实就是构造函数Object的复制,A的实例就是Object的实例。
第二种特殊情况,不存在任何继承。
class A {}
A.__proto__ === Function.prototype; // true
A.prototype.__proto__ === Object.prototype; // true
这种情况下,A作为一个基类(即不存在任何继承),就是一个普通函数,所以直接继承Funciton.prototype。但是,A调用后返回一个空对象(即Object实例),所以A.prototype.__proto__指向构造函数(Object)的prototype属性。
第三种特殊情况,子类继承null
class A extends null {
}
A.__proto__ === Function.prototype; // true
A.prototype.__proto__ === undefined; // true
这种情况与第二种情况非常像。A也是一个普通函数,所以直接继承Funciton.prototype。但是,A调用后返回的对象不继承任何方法,所以它的__proto__指向Function.prototype,即实质上执行了下面的代码。
class C extends null {
constructor() {
return Object.create(null);
}
}
Object.getPrototypeOf()
Object.getPrototypeOf方法可以用来从子类上获取父类。
Object.getPrototypeOf(A) === B;
因此,可以使用这个方法判断,一个类是否继承了另一个类。
super关键字
super这个关键字,有两种用法,含义不同。
- 作为函数调用时(即
super(...args)),super代表父类的构造函数 - 作为对象调用时(即
super.prop或super.method()),super代表父类。注意,此时super即可以引用父类实例的属性和方法,也可以引用父类的静态方法
class B extends A {
get m() {
return this._p * super._p;
}
set m() {
throw new Error('该属性只读');
}
}
上面代码中,子类通过super关键字,调用父类实例的_p属性。
由于,对象总是继承其他对象的,所以可以在任意一个对象中,使用super关键字
var obj = {
toString() {
return "MyObject: " + super.toString();
}
};
obj.toString(); // MyObject: [object Object]
生命不息,折腾不止...
I'm not a real coder, but i love it so much!