「继承」在面向对象编程中属于核心概念,不过 JavaScript 并不是严格意义上的面向对象语言,JS 不像传统的面向对象编程语言例如 Java 那样,倾向于通过先定义类,定义继承关系,再创建对象,而是使用了另一种概念: 「原型」和「原型链」实现了类似继承的特性。
创建构造函数 👨🔧
在 ES6 以前,由于没有class
关键字,我们会使用构造函数来创建对象。
1
2
3
4
5
6
7
8
9
|
//声明一个构造函数,用于创建人类
function Person(firstName, lastName) {
this.FirstName = firstName || "unknown"
this.LastName = lastName || "unknown"
};
Person.prototype.getFullName = function () {
return this.FirstName + " " + this.LastName
}
|
在上面的例子中,我们定义了一个用于构造Person
对象的构造函数,包含了私有属性FirstName
、LastName
以及共有的函数getFullName
。
创建 Person 对象 🙎♂️
定义了构造函数之后,我们可以使用 JS 提供的new
关键字来创建出 Person 对象。
1
2
|
let student1 = new Person("zhang","san")
let student2 = new Person("li","si")
|
实际上,new
关键字在背后偷偷做了几件事:
- 自动创建空对象
- 自动为空对象关联原型,原型地址指定为 Person.prototype
- 将 this 指定为刚刚创建的空对象
- 自动 return this
所以,student1和student2这两个对象的原型都是 Person.prototype ,来证明一下:
1
|
student1.getFullName === student2.getFullName //true
|
继承 Person 对象 👨🎓
现在我们可以考虑一下,有没有办法可以定义一个对象拥有 Person 属性的同时又可以拥有自己的属性,例如 Student 对象,除了姓名外,还有学校,成绩等属性。实现这个过程就叫做「继承」。
1
2
3
4
5
6
7
8
9
10
|
function Student(firstName, lastName, schoolName, grade){
//构造函数绑定,将父类的构造函数绑定在子类上
Person.call(this, firstName, lastName)
//定义子类私有属性
this.SchoolName = schoolName || "unknown"
this.Grade = grade || 0
}
//Student.prototype = Person.prototype
Student.prototype = new Person()
Student.prototype.constructor = Student
|
这种继承方式一般叫做「组合继承」,即通过调用父类构造,继承父类的属性并保留传参的优点,然后通过将父类实例作为子类原型,实现了函数复用。
创建对象:
1
2
3
4
5
6
|
let student1 = new Student("zhang","san","MIT","100")
let student2 = new Student("li","si","MIT","60")
console.log(student1.getFullName()) //zhangsan
console.log(student1 instanceof Student) //true
console.log(student1 instanceof Person) //true
|
ES6 面向对象 👼
在 ES6 之后,JavaScript 引入了class
和extends
关键字,变得跟 Java 更像了😵,使用这两个关键字可以更加简洁地用 JavaScript 实现类的定义和继承,不过这仅仅是一个语法糖
,JavaScript 的面向对象今天依然是基于 Prototype 实现的。
定义 Person 类
1
2
3
4
5
6
7
8
9
|
class Person{
constructor(firstName, lastName) {
this.firstName = firstName || "unknown"
this.lastName = lastName || "unknown"
}
getFullName(){
return this.firstName + " " + this.lastName
}
}
|
继承 Person 类
1
2
3
4
5
6
7
8
9
10
|
class Student extends Person{
constructor(firstName, lastName, schoolName, grade) {
super(firstName, lastName)
this.schoolName = schoolName
this.grade = grade
}
getFullName(){
return this.firstName + " " + this.lastName + " " + this.schoolName + " " + this.grade
}
}
|
创建 Student 对象:
1
2
3
4
5
6
|
let student1 = new Student("zhang","san","MIT","100")
let student2 = new Student("li","si","MIT","60")
console.log(student1.getFullName()) //zhang san MIT 100
console.log(student1 instanceof Student) //true
console.log(student1 instanceof Person) //true
|
结果跟使用原型链实现的继承是一样的。
(完)
参考
JavaScript 继承机制的设计思想 - 阮一峰