ES6继承
class通过extends关键字继承
写法如下
calss People {
}
class Man extends People{
constructor(name,age,eat){
super(name,age)
this.eat = eat
}
showEat(){
return this.eat + ' ' + super.toString()
//调用父类的toString方法
}
}
复制代码
上面代码定义了一个Man类,该类通过extends关键字,继承了People类的所有属性和方法。
super关键字,表示父类的构造函数,用来新建父类的this对象
,子类必须在constructor
方法中调用super
方法,否则新建实例时会报错,因为子类自己的this
对象,必须通过父类的构造函数才能塑造,得到与父类相同的实例属性和方法,然后在进行加工,加上子类自己的实例属性和方法,如果不调用super
方法,子类就得不到this
对象
class People {
}
class Man extends People{
constructor(){
}
}
let m1 = new Man()
复制代码
如果子类没有定义constructor方法,这个方法会被默认添加
class ColorPoint extends Point {
}
// 等同于
class ColorPoint extends Point {
constructor(...args) {
super(...args);
}
}
复制代码
在子类的构造函数中,调用super之后,才可以使用this
关键字,
class People {
constructor(name) {
this.name = name;
}
}
class ColorPoint extends Point {
constructor(name,age) {
this.age = age; // ReferenceError
super(name);
this.age = age; // 正确
}
}
复制代码
生成实例后
let m1 = new Man('yr', 18);
m1 instanceof Man // true
m1 instanceof People // true
复制代码
m1同时是Man和People两个类的实例,与ES5的一致 父类的静态方法,也会被子类继承
class A {
static hello() {
console.log('hello world');
}
}
class B extends A {
}
B.hello() // hello world
复制代码
Object.getPrototypeOf
Object.getPrototypeOf方法可以用来从子类上获取父类。
Object.getPrototypeOf(ColorPoint) === Point
复制代码
可以使用这个方法判断,一个类是否继承了另一个类。
super关键字
super
关键字,可以当函数使用,也可以当对象使用 第一种当函数调用时,代表父类的构造函数,ES6要求,子类的构造函数中必须执行一次super
函数
class A {}
class B extends A {
constructor() {
super();
}
}
复制代码
子类B的构造函数之中的super(),代表调用父类的构造函数。这是必须的,否则 JavaScript 引擎会报错
super
虽然代表了父类A的构造函数,但是返回的是子类B的实例,即super
内部的this
指的是B的实例,因此super()
在这里相当于A.prototype.constructor.call(this)
作为函数时,super()
只能用在子类的构造函数之中,用在其他地方就会报错
第二种情况,super
作为对象时,在普通方法中,指向父类的原型对象;在静态方法中,指向父类。
class A {
p() {
return 2;
}
}
class B extends A {
constructor() {
super();
console.log(super.p()); // 2
}
}
let b = new B();
复制代码
子类B当中的super.p()
,就是将super当作一个对象使用。这时,super
在普通方法之中,指向A.prototype
,所以super.p()
就相当于A.prototype.p()
由于super指向父类的原型对象,所以定义在父类实例上的方法或属性,是无法通过super调用的。
class A {
constructor() {
this.p = 2;
}
}
class B extends A {
get m() {
return super.p;
}
}
let b = new B();
b.m // undefined
复制代码
p是父类A实例的属性,super.p
就引用不到它,如果属性定义在父类的原型对象上,super
就可以取到。
class A {}
A.prototype.x = 2;
class B extends A {
constructor() {
super();
console.log(super.x) // 2
}
}
let b = new B();
复制代码
在子类普通方法中通过super
调用父类的方法时,方法内部的this指向当前的子类实例。
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() // 2
复制代码
super.print()
虽然调用的是A.prototype.print()
,但是A.prototype.print()
内部的this
指向子类B的实例,导致输出的是2,而不是1。也就是说,实际上执行的是super.print.call(this)
由于this
指向子类实例,所以如果通过super
对某个属性赋值,这时super
就是this
,赋值的属性会变成子类实例的属性。
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();
复制代码
super.x
赋值为3
,等同于对this.x
赋值为3。而当读取super.x
的时候,读的是A.prototype.x
,所以返回undefined
。
如果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
调用父类的方法时,方法内部的this
指向当前的子类,而不是子类的实例。
class A {
constructor() {
this.x = 1;
}
static print() {
console.log(this.x);
}
}
class B extends A {
constructor() {
super();
this.x = 2;
}
static m() {
super.print();
}
}
B.x = 3;
B.m() // 3
复制代码
静态方法B.m
里面,super.print
指向父类的静态方法。这个方法里面的this
指向的是B
,而不是B
的实例。
使用super
的时候,必须显式指定是作为函数、还是作为对象使用,否则会报错。
console.log(super); // 报错
复制代码
类的prototype属性和__proto__属性
大多数浏览器的 ES5
实现之中,每一个对象都有__proto__
属性,指向对应的构造函数的prototype
属性。Class
作为构造函数的语法糖,同时有prototype
属性和__proto__
属性,因此同时存在两条继承链。
(1)子类的__proto__
属性,表示构造函数的继承,总是指向父类 (2)子类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
属性。
类的继承是按照下面的模式实现的
class A {
}
class B {
}
// B 的实例继承 A 的实例
Object.setPrototypeOf(B.prototype, A.prototype);
// B 继承 A 的静态属性
Object.setPrototypeOf(B, A);
const b = new B();
复制代码
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)的原型__proto__属性是父类(A);作为一个构造函数,子类(B)的原型对象(prototype属性)是父类的原型对象(prototype属性)的实例。
第一种,子类继承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作为一个基类(即不存在任何继承),就是一个普通函数,所以直接继承 Function.prototype
但是,A调用后返回一个空对象(即Object实例),所以A.prototype.__proto__
指向构造函数Object
的prototype
属性
实例的__proto__属性
子类实例的_proto__
性的_proto__
性,指向父类实例的_proto__
性。也就是说,子类的原型的原型,是父类的原型。
var m1 = new People('yr');
var m2 = new Man('yr',18);
m2.__proto__ === m1.__proto__ // false
m2.__proto__.__proto__ === m1.__proto__ // true
复制代码
ES5 是先新建子类的实例对象this,再将父类的属性添加到子类上
ES6 子类自己的this对象,必须先通过父类的构造函数完成塑造,得到与父类同样的实例属性和方法,然后再对其进行加工,加上子类自己的实例属性和方法
还没有评论,来说两句吧...