基本用法
class之间可以通过extends关键字实现继承,写法上比es5更加清晰
class Point{}
class FriendPoint extends Point{ //FriendPoint 继承了Point类的所有属性和方法
constructor(x,y,name){
super(x,y) //调用父类的constructor(x,y)
this.friend= color
}
toString(){
return this.friend+ '' + super.toString() //调用父类的的toString
}
}
在子类中出现了super关键字 表示父类的构造函数,用来创建父类的this。子类没有自己的this,必须在constructor中调用调用super方法,继承父类的this,然后对其加工,否则会报错,没有this对象
class FriendPoint extends Point{
constructor(){
}
}
let friend = new FriendPoint() // Must call super constructor in derived class before accessing 'this' or returning from derived constructor
es5的继承是先创建子类的this 再将父类的this添加到子类的this上面。es6是先创建父类的实例对象的this 先调用super方法。然后再用子类的构造函数修改父类的this,要不不能使用this关键字,会报错
类的prototype属性和__proto__属性
es5中每个对象都有一个__proto__属性 指向对应的构造函数的prototype属性
class作为构造函数的语法糖 也有prototype和__proto__属性,同时存在着两条继承链
class A{}
class B extends A{
constructor(){
super()
}
}
let ob = new B()
console.log(B.__proto__) //class A
console.log(B.__proto__ === A) //true
console.log(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)
就是可以理解为
作为一个对象 子类B的原型 (__proto__属性) 指向的是父类A,B.proto = A 比如说一个构造函数的原型对象可能还有一个__proto__指向object
作为一个构造函数 子类B的原型(prototype属性)就是父类A的实例B.prototype.proto = A.prototype
继承目标
只要父类(class B extend A{}) A只要是一个有prototype属性的函数都能被B继承 除了Function.prototype函数,Function.prototype.prototype是undefined,还有三种特殊情况
// 可以继承Object类
class C extends Object{
}
console.log(C.__proto__ === Object) //true
console.log(C.prototype.__proto__ === Object.prototype) //true
//不做任何继承
class C{
}
//C就相当与普通的构造函数对象 相当于new Function() 所以
console.log( C.__proto__ === Function.prototype) //true
//普通的构造函数对象的__proto__就是Object.prototype
//比如
function C(){
}
console.log(C.prototype.__proto__ == Object.prototype) //true
//第三种继承null
class C extends null {
}
// C也是一个普通函数,所以直接继承Funciton.prototype
C.__proto__ === Function.prototype // true
// 但是A.原型对象不继承任何方法 相当于Object.setPrototypeOf(C.prototype,null)
C.prototype.__proto__ === undefined // true
//相当于
function C(){
}
Object.setPrototypeOf(C.prototype,null)
console.log(C.prototype.__proto__ === undefined) //true
super关键字
super可以作为函数调用 也可以作为对象使用 而且必须是显式的作为函数或者是对象使用
class A{}
class B extends A{
constructor(){
super()
//console.log(super) //'super' keyword unexpected here 无法看出是作为函数还是对象使用
console.log(super.valueOf()) //返回的是一个对象 说明 super是一个对象 所以不会报错
}
}
new B()
作为函数调用 代表父类的构造函数
class A {
constructor(){
console.log(new.target.name) //指向正在执行的函数 new B()的时候指向的B
}
}
class B extends A {
constructor() {
super(); //必须的,代表调用父类的构造函数 否则会报错 相当于A.prototype.constructor.call(this)
}
m(){
super() //'super' keyword unexpected here作为函数只能用在子类的构造函数之中 否则会报错
}
}
new B()
作为对象调用 指向的是父类的原型对象
class A {
constructor(){
this.a = 2
this.x = 3
}
m(){
return 1
}
point(){
return this.x
}
}
class B extends A{
constructor(){
super()
this.x = 4
console.log(super.m()) //1
console.log(super.a) //undefined 访问不到实例属性
//只能访问到原型属性 相当于 A.prototype
console.log(super.point()) //4
//通过super调用父类的方法的时候 super绑定的是子类的this 实际上执行的是super.point.call(this),所以打印的是子类实例的this
super.x = 5
console.log(super.x) //undefined
console.log(this.x) //5
//由于super绑定的是子类的this,所以相当于this.x =5 相当于给子类实例的x赋值
}
}
let b = new B()
实例的__proto__属性
class A{}
class B extends A{
constructor(){
super()
}
}
let a = new A()
let b = new B()
// 因为 b.__proto__属性是 B.prototype
// 所以
console.log(b.__proto__.__proto__ === A.prototype) //true
// 所以
console.log(b.__proto__.__proto__ === a.__proto__) //true
//所以通过子实例的__proto__.__proto__属性 能修改父类实例的行为
b.__proto__.__proto__.getName = function(){
return 2
}
console.log(a.getName()) //2 会先在实例上找这个方法 找不到就去原型上找