持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第 27 天,点击查看活动详情
学习 class 中的集成
start
- 前面学习了 class 的基础使用。点击这里
- 今天继续学习一下 class 实现的继承。
基础知识
什么是类
类是指用来创造一类对象的模板,而通过这个模板创建出来的对象叫做实例。
什么是继承
继承是一种类(class)与类之间的关系,JS 中没有类,但是可以通过构造函数模拟类,然后通过原型来实现继承,
- 继承是为了实现数据共享,js 中的继承当然也是为了实现数据共享。
- 继承是子类继承父类的特征或者行为,使子类也具有父类的属性和方法;
- 或者子类从父类继承方法,使得子类具有父类相同的行为
- 继承可以使得子类具有父类的各种属性和方法,而不需要再次编写相同的代码。
ES5 中使用最广泛的 组合继承
function Person(name) {
this.name = name
this.colors = ['red', 'blue', 'green']
}
Person.prototype.sayName = function () {
console.log(this.name)
}
function Son(name, age) {
Person.call(this, name)
this.age = age
}
var s1 = new Son('大头儿子', 8)
console.log(s1)
// Son { name: '大头儿子', colors: [ 'red', 'blue', 'green' ], age: 8 }
梳理一下上述代码的逻辑
- 首先定义了一个构造函数
Person
; - 然后在
Person
的原型上添加方法; - 定义一个子类, 构造函数 Son;
- 在
new Son('大头儿子', 8)
的过程中:- new 创建了一个空对象
s1 = {}
。 - 对象
s1
的隐式原型指向函数的显式原型(Son.prototype) - 函数中的 this 指向这个对象
s1
&& 运行函数运行函数的过程中执行了
Person.call(this, name)
,所以会给s1.name
,s1.colors
赋值。 new Son()
返回结果 ,Son 函数有返回对象,输出这个对象; Son 函数没有返回对象,输出 new 出来的对象s1
。- 输出对象
s1
- new 创建了一个空对象
- 打印的
s1
中就包含name,colors
两个属性;
class 中的继承
Class 可以通过 extends
关键字实现继承,让子类继承父类的属性和方法。
1. 基础使用
演示代码:
class Person {}
class Son extends Person {
constructor(x, y, color) {
super(x, y) // 调用父类的constructor(x, y)
this.color = color
}
toString() {
return this.color + ' ' + super.toString() // 调用父类的toString()
}
}
注意事项:
-
extends
英文释义:继承。 -
super
在这里表示父类的构造函数,用来新建一个父类的实例对象。 -
ES6 规定,子类必须在
constructor()
方法中调用super()
,否则就会报错。-
子类自己的 this 对象,必须先通过父类的构造函数完成塑造,得到与父类同样的实例属性和方法,然后再对其进行加工,添加子类自己的实例属性和方法。如果不调用 super()方法,子类就得不到自己的 this 对象。
-
所以如果,在
super
之前使用this
也会报错:class Person {} class Son extends Person { constructor(x, y, color) { this.color = color super(x, y) // 调用父类的 constructor(x, y) } } new Son(1, 2, 3) // ReferenceError: Must call super constructor in derived class before accessing 'this' or returning from derived constructor
-
-
对比 ES5 的继承
- ES5 的继承机制,是先创造一个独立的子类的实例对象,然后再将父类的方法添加到这个对象上面;
- ES6 的继承机制,则是先将父类的属性和方法,加到一个空的对象上面,然后再将该对象作为子类的实例;
2. 类的静态属性会被继承
演示代码:
class Person {
static boo = 123
static foo = { num: 10 }
}
class Son extends Person {}
console.log(Son.boo) // 123
console.log(Son.foo) // { num: 10 }
注意事项:
-
静态属性其实就是
Person
自身的属性,例如:Person.boo = 123
。 -
注意静态属性是继承到子类上,不是子类的实例上。
-
继承的方式是浅拷贝。
class Person { static boo = 123 static foo = { num: 10 } } class Son extends Person {} class Jemi extends Person {} console.log(Son.boo) // 123 console.log(Son.foo) // { num: 10 } console.log(Jemi.boo) // 123 console.log(Jemi.foo) // { num: 10 } Son.boo++ Son.foo.num++ console.log(Son.boo) // 124 console.log(Son.foo) // { num: 11 } console.log(Jemi.boo) // 123 console.log(Jemi.foo) // { num: 11 }
3. 获取子类的父类
演示代码:
class Person {}
class Son extends Person {}
console.log(Object.getPrototypeOf(Son)) // [Function: Person]
console.log(Object.getPrototypeOf(Son) === Person) // true
4. super 的使用
第一种情况,super 作为函数调用时:
代表父类的构造函数。
演示代码:
class A {}
class B extends A {
constructor() {
super()
}
}
// super虽然代表了父类A的构造函数,但是返回的是子类B的实例
注意事项:
super
以函数的形式调用:主要就是在子类的constructor()
调用。
尝试了其他几种方式使用,都会报错:
- 父类中使用
super()
- 子类的静态属性中使用
super()
- 子类原型上的属性中使用
super()
第二种情况,super 作为对象时:
在普通方法中,指向父类的原型对象,this 指向当前的子类实例;在静态方法中,指向父类,this 指向当前的子类。
简单来说就是
super.xxx
的形式。
演示代码:
class A {
constructor() {}
static foo() {
console.log('A父类自身的foo方法', this.a)
}
foo() {
console.log('A父类的原型上的foo方法', this.a)
}
}
class B extends A {
static a = 111
a = 222
constructor() {
super()
}
static say() {
super.foo()
}
say() {
super.foo()
}
}
var b1 = new B()
/* 1 普通方法中的super以对象的形式使用,指向父类的原型,this指向子类的实例。*/
b1.say() // A父类的原型上的foo方法 222
/* 2 静态方法中的super以对象的形式使用,指向父类的原型,this指向子类的实例。*/
B.say() // A父类自身的foo方法 111
解释上述案例:
-
基础知识储备
-
constructor(x, y) {}
等同于 ES5 中的function A(x,y){}
-
static foo
等同于 ES5 中的A.foo
-
foo
等同于 ES5 中的A.prototype.foo
-
-
注意这里的
super
是采取对象的形式使用。 -
b1.say()
b1
自身中没有say
;- 沿着原型链,找到
B.prototype
上有say
; super.foo()
,普通方法的super
指向父类原型,也就是A.prototype
;- 所以打印:
A父类的原型上的foo方法
; - 后续的
this
,谁调用就指向谁,所以this
指向b1
,b1.a 222
;
-
B.say()
B.say
,调用的是B 中的 static say
super.foo()
,静态方法的super
指向父类,也就是A
;super.foo()
,静态方法的super
指向父类,也就是A
;- 所以打印:
A父类自身的foo方法
; - 后续的
this
,谁调用就指向谁,所以this
指向B
,B.a 111
;
end
- 完结。