# OOP:初探面向对象

# 为什么需要面向对象

程序的本质,是在模拟现实世界,解决现实世界中的问题。之前我们学习了 js 的一些基本语法和内置对象。

但掌握了面向对象思想后,你学到的这些知识才会发挥出他们的最大威力。

# 何为面向对象

  1. 类:是程序对世界的抽象总结,总结出某一类事物的总体特征。
  2. 实例:通过之前总结出来的特征来构建一个真实的对象。

比如要抽象出一个学生,那么他们的特征是:

- 【年龄】
- 【性别】
- 母亲
- 父亲
- 班主任
- 以及班级

但如果想要抽象卡车司机,那么他们的特征应该是:

- 【年龄】
- 【性别】
- 驾龄
- 驾照
- 视力
- 驾驶违规记录
- 月薪

那么我们该怎样在程序中描述这两种人呢?

// 学生
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;
}

以上是我们抽象出来的两种人物的特征,我们可以把特征 归类 。不同的 描述不同的人群。

TruckDriverStudent 是不同人群的模型。我们称之为 类名

那么在现实世界中,每一类人群都有自己的 动作。比如学生早上要去上学,下课期间还可以去商店买东西,放学后父母接她回家。

卡车司机也是一样,从城市 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 关键字加函数名。此时的函数叫做构建函数。

# 继承

上文定义了两种类型,TruckDriverStudent。但这两种类型都同属于 类。 假如现在要给这两个类增加吃饭的 方法。此时我们就可以在抽象一个 类来满足需求

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 正在吃饭

我们通过给构造函数设置原型链的方式实现了继承,下一篇文章我们会详细讲继承方面的内容。这里只需要理解继承的作用。

# 多态

现在我们要给这两个职业 TruckDriverStudent 加一个共同的方法休息。那么对于 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 的动态类型,原型链等内容。

上次更新: 9/1/2020, 1:40:55 PM