# OOP:初探面向对象
# 为什么需要面向对象
程序的本质,是在模拟现实世界,解决现实世界中的问题。之前我们学习了 js 的一些基本语法和内置对象。
但掌握了面向对象思想后,你学到的这些知识才会发挥出他们的最大威力。
# 何为面向对象
- 类:是程序对世界的抽象总结,总结出某一类事物的总体特征。
- 实例:通过之前总结出来的特征来构建一个真实的对象。
比如要抽象出一个学生,那么他们的特征是:
- 【年龄】
- 【性别】
- 母亲
- 父亲
- 班主任
- 以及班级
但如果想要抽象卡车司机,那么他们的特征应该是:
- 【年龄】
- 【性别】
- 驾龄
- 驾照
- 视力
- 驾驶违规记录
- 月薪
那么我们该怎样在程序中描述这两种人呢?
// 学生
function Student(age, gender, name, mother, father, headmaster, className) {
// 年龄
this.age = age;
// 性别
this.gender = gender;
// 姓名
this.name = name;
// 母亲
this.mother = mother;
// 父亲
this.father = father;
// 班主任
this.headmaster = headmaster
// 班级名称
this.className = className;
}
// 卡车司机
function TruckDriver(opt) {
opt = opt || {}
// 年龄
this.age = opt.age;
// 性别
this.gender = opt.gender;
// 姓名
this.name = opt.name;
// 驾龄
this.drivingExperience = opt.drivingExperience;
// 驾照
this.drivingLicense = opt.drivingLicense;
// 视力
this.vision = opt.vision;
// 驾驶违规记录
this.drivingViolationRecord = opt.drivingViolationRecord;
// 月薪
this.salary = opt.salary;
}
以上是我们抽象出来的两种人物的特征,我们可以把特征 归类
。不同的 类
描述不同的人群。
TruckDriver
和 Student
是不同人群的模型。我们称之为 类名
那么在现实世界中,每一类人群都有自己的 动作
。比如学生早上要去上学,下课期间还可以去商店买东西,放学后父母接她回家。
卡车司机也是一样,从城市 A 出发驾车到码头。然后把集装箱装上货船再驾车去停车场。
这一系列的动作我们都可以用程序简单或复杂的描述出来。
现在我们为刚刚出现的两个 类
增加 动作
。
function Student(age, gender, name, mother, father, headmaster, className) {
// 省略 ...
}
// 学生可以做的动作
Student.prototype.listening = function() {
console.log(`${this.name} 正在听课...`)
}
Student.prototype.buy = function() {
console.log(`${this.name} 正在买东西...`)
}
Student.prototype.goHome = function() {
console.log(`${this.name} 父母接他回家`)
}
// 卡车司机
function TruckDriver(opt) {
// 省略 ...
}
// 卡车司机可以做的动作
TruckDriver.prototype.setOut = function() {
console.log(`卡车司机 ${this.name} 出发了...`)
}
TruckDriver.prototype.load = function() {
console.log(`卡车司机 ${this.name} 正在码头...`)
}
TruckDriver.prototype.goParking = function() {
console.log(`卡车司机 ${this.name} 去停车场...`)
}
我们已经为这两个 类
加上了他们自己的 动作
,其中这个 动作
,我们称之为 类
的 方法
。
并且我们定义类的这个动作叫做 封装
。这是面向对象中的首要概念。
现在我们以卡车司机 类
为例。克隆出两个卡车司机来(本质上卡车司机类是可以克隆无限多个卡车司机的,这就像是一个工厂的模具)。
let tdA = new TruckDriver({
age: 30,
gender: '男',
name: '司机A',
drivingLicense: 'B1'
})
tdA.setOut() // 卡车司机 司机A 出发了...
let tdB = new TruckDriver({
age: 35,
gender: '女',
name: '司机B',
drivingLicense: 'B2'
})
tdB.goParking() // 卡车司机 司机B 去停车场...
以上我们提到的 new
关键字加函数名。此时的函数叫做构建函数。
# 继承
上文定义了两种类型,TruckDriver
和 Student
。但这两种类型都同属于 人
类。
假如现在要给这两个类增加吃饭的 方法
。此时我们就可以在抽象一个 人
类来满足需求
function Person() {
// something
}
Person.prototype.eat = function() {
console.log(`${this.name} 正在吃饭`);
}
// `学生` 继承 `人` 类的原型
Student.prototype = Person.prototype
let s = new Student(
18, '男', '石兴龙'
);
s.eat() // 石兴龙 正在吃饭
// `卡车司机` 继承 `人` 类的原型
TruckDriver.prototype = Person.prototype
let tdA = new TruckDriver({
age: 30,
gender: '男',
name: '司机A',
drivingLicense: 'B1'
})
tdA.eat() // 司机A 正在吃饭
我们通过给构造函数设置原型链的方式实现了继承,下一篇文章我们会详细讲继承方面的内容。这里只需要理解继承的作用。
# 多态
现在我们要给这两个职业 TruckDriver
和 Student
加一个共同的方法休息
。那么对于 TruckDriver
来说,他想要休息得先把车开到停车场,然后去旅馆休息。对于 Student
来说,只要不上课就可以任意休息。
你发现了么?虽然这两个类都有 休息
的方法,但是表现出来的结果却不一样。这就是 多态
。我们用代码实现以下
function Person() {
// something
}
Person.prototype.rest = function() {
// 空方法 等待子类去实现
}
// 继承 Person 构造函数的 原型
TruckDriver.prototype = Person.prototype
TruckDriver.prototype.rest = function() {
console.log('1. 先把车开到停车场')
console.log('2. 然后找到旅馆休息')
}
// 继承 Person 构造函数的 原型
Student.prototype = Person.prototype
Student.prototype.rest = function() {
console.log('现在是暑假,累了就可以任意休息')
}
new TruckDriver().rest()
// 1. 先把车开到停车场
// 2. 然后找到旅馆休息
new Student().rest()
// 现在是暑假,累了就可以任意休息
我们看到两个类调用同样的函数,但表现结果却是不一样的。这就是 多态
#
程序是在模拟现实世界,面向对象是很好的方法。
本章内容带你熟悉了面向对象中最重要的 封装
,继承
,多态
的概念。后面会讲到 js 的动态类型,原型链等内容。