# JavaScript教程 - 9 面向对象
面向对象编程,是一种编程思想,简单的理解就是:先有模板,也就是类,根据模板去创建实例,也就是对象,然后使用对象来完成功能开发。
我们经常说面向过程编程和面向对象编程,面向过程编程关注的是实现功能的步骤,面向对象编程更关注的是谁来实现功能。
面向对象编程有3大特性:
- 封装
- 继承
- 多态
下面依次开始讲起。
# 9.2 封装
在前面我们创建类,在类中定义了属性和方法,通过属性和方法来对现实世界的事物进行抽象的描述。
一个事物有很多的属性和方法,但是并不是所有属性和方法都需要开放出来。例如我们定义了一个手机类,我们可以使用手机打电话、拍照等,但是我们并不关心手机电压,驱动信息,也不关心内存的分配,CPU的调度等,虽然这些都属于手机的属性和行为。
我们可以将用户不关心的属性和方法封装并隐藏起来,只给类内部的方法调用,例如上网会用到4G模块,但是不是由用户来使用4G模块,而是由手机上网的功能来调用4G模块,只开放用户直接使用的信息和功能。
那么回过头来看,什么是封装?
它指的是将对象的状态信息隐藏在对象内部,不允许外部程序直接访问对象的内部信息。如果要访问这些信息,需要通过该类所提供的方法来实现对内部信息的操作和访问。
# 1 私有属性和方法
那么怎么将不想暴露的属性和方法隐藏起来呢,就需要用到私有属性和私有方法。
定义私有属性和方法,使用 #
开头来定义。
举个栗子:
class Phone {
#voltage; // 私有属性需要显式声明
constructor(producer, voltage) {
this.producer = producer; // 生产商
this.#voltage = voltage; // 电压
}
// 定义公共方法
call() {
console.log("打电话");
this.#getRunVoltage(); // 在类内部可以调用私有方法
}
// 定义一个私有方法
#getRunVoltage() {
console.log("当前电压:" + this.#voltage);
}
}
let phone = new Phone("小米", 12);
console.log(phone.producer); // 小米,调用公有属性
// console.log(phone.#voltage); // 报错:私有变量,类外部无法访问
phone.call(); // 调用公有方法
// phone.#getRunVoltage(); // 报错,私有方法,类外部无法访问
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
- 上面定义了私有属性
#voltage
和私有方法#getRunVoltage()
,私有属性和私有方法只能在类内部的方法访问,在类的外部不能通过对象来调用。 - 需要注意,私有方法需要显式声明。
- 静态属性和方法,同样在属性和方法前添加
#
即可。
如果不想暴露的属性和方法可以定义为私有成员。私有属性只能在类内部的方法中调用,不能通过对象来调用。
# 2 getter和setter
我们前面说到,封装是将对象的状态信息隐藏在对象内部,不允许外部程序直接访问对象的内部信息。如果要访问这些信息,需要通过该类所提供的方法来实现对内部信息的操作和访问。
我们一般会针对私有属性提供两个对应的方法,分别用来获取和设置对应的属性,这两个方法称为 getter 和 setter 方法。
举个栗子:
class Phone {
#voltage; // 私有属性需要显式声明
constructor(producer, voltage) {
this.producer = producer;
this.#voltage = voltage;
}
// voltage属性的getter
getVoltage() {
return this.#voltage;
}
// voltage属性的setter
setVoltage(voltage) {
// 还可以在设置的时候进行判断
if (voltage > 36 || voltage < 0) {
return;
}
this.#voltage = voltage;
}
}
let phone = new Phone('小米', 12);
phone.setVoltage(20); // 调用 setter 设置值
console.log(phone.getVoltage()); // 调用 getter获取值
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
- 在上面为私有属性
voltage
定义了 get 和 set 方法,get
方法以get
开头,后跟属性名,遵循方法命名规则。set
方法类似,get 方法有返回值,没有参数,set 方法接收参数,没有返回值。 - 我们还可以根据需求,在设置或获取属性值的时候,对属性值进行额外的处理,例如格式验证。
- 如果有多个私有属性,根据需要,可以为每个私有属性添加 getter 和 setter 。
通过 getter 和 setter 来访问私有属性和为私有属性赋值。这样做的话,严格控制了属性获取和设置的入口,如果通过 对象.属性
来修改,代码很多的时候完全会不知道在哪里修改了属性导致出现了问题。
上面的 getter 和 setter 在进行属性访问的时候,需要调用方法,有点麻烦。JavaScript 还提供了访问器属性方式的 getter 和 setter。
举个栗子:
class Phone {
#voltage; // 私有属性需要显式声明
constructor(producer, voltage) {
this.producer = producer;
this.#voltage = voltage;
}
// voltage属性的getter
get voltage() {
console.log('调用了getter');
return this.#voltage;
}
// voltage属性的setter
set voltage(voltage) {
console.log('调用了setter', voltage);
this.#voltage = voltage;
}
}
let phone = new Phone('小米', 12);
phone.voltage = 20; // 以属性的方式访问,其实调用的是voltage的setter方法
console.log(phone.voltage); // 调用voltage的getter方法
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
- 在上面的代码中,通过
get或set 关键字 + 属性名
构成的 getter 和 setter,可以以属性访问的方式来调用方法。 phone.voltage = 20;
调用的是set voltage()
方法。
# 9.3 继承
在现实世界,有麻雀和鸽子,它们都属于鸟类,麻雀、鸽子和鸟类的关系是父类和子类的关系。
麻雀和鸽子都会飞,我们可以在麻雀类中定义一个飞的方法,在鸽子类中定义一个飞的方法,但是这两个飞的方法是一样的,都是用翅膀飞。那么我们可以直接在鸟类中定义一个飞的方法,让麻雀和鸽子类都继承这个鸟类,那么它们就拥有了飞的方法,就不用重复再定义了。