JS中的面向对象

JS 是一种基于对象的语言, 在 JavaScript 中一切皆可看做对象.但是 js 的对象模型和大多数面向对象语言的对象模型又不一样. 接下来我们从多态、封装、继承三个基本特征来理解 JS 的面向对象思想

本文内容主要是 copy这位仁兄的, 解释的很到位就照搬过来了.

多态

含义

同一操作作用于不同的对象上面,可以产生不同的解释和不同的执行结果,也就是说,给不同的对象发送同一个消息时,这些对象会根据这个消息分别给出不同的反馈。

举个例子:假设家里养了一只猫和一只狗,两只宠物都要吃饭,但是吃的东西不太一样,根据主人的吃饭命令,猫要吃鱼,狗要吃肉,这就包含了多态的思想在里面,用 JS 代码来看就是:

1
2
3
4
5
6
7
8
9
10
11
12
13
let petEat = function (pet) {
pet.eat();
};
let Dog = function () {};
Dog.prototype.eat = function () {
console.log("吃肉");
};
let Cat = function () {};
Cat.prototype.eat = function () {
console.log("吃鱼");
};
petEat(new Dog());
petEat(new Cat());

上面这段代码展示的就是对象的多态性,由于 JS 是一门动态类型语言,变量类型在运行时是可变的,因此一个 JS 对象既可以是 Dog 类型的对象也可以是 Cat 类型的对象,JS 对象多态性是与生俱来的,而在静态类型语言中,编译时会进行类型匹配检查,如果想要一个对象既表示 Dog 类型又表示 Cat 类型在编译的时候就会报错,当然也会有解决办法,一般会通过继承来实现向上转型,这里感兴趣的可以去对比一下静态语言的对象多态性。

作用

多态的作用是通过把过程化的条件分支语句转化为对象的多态性,从而消除这些条件分支语句,举个例子:还是上面宠物吃饭的问题,如果在没有使用对象的多态性之前代码可能是这样是的:

1
2
3
4
5
6
7
8
9
10
11
let petEat = function (pet) {
if (pet instanceof Dog) {
console.log("吃肉");
} else if (pet instanceof Cat) {
console.log("吃鱼");
}
};
let Dog = function () {};
let Cat = function () {};
petEat(new Dog());
petEat(new Cat());

通过条件语句来判断宠物的类型决定吃什么,当家里再养金鱼,就需要再加一个条件分支,随着新增的宠物越来越多,条件语句的分支就会越来越多,按照上面多态的写法,就只需要新增对象和方法就行,解决了条件分支语句的问题

封装

封装的目的是将信息隐藏,一般来说封装包括封装数据、封装实现,接下来就逐一来看:

由于 JS 的变量定义没有 private、protected、public 等关键字来提供权限访问,因此只能依赖作用域来实现封装特性.

封装实现即隐藏实现细节、设计细节,封装使得对象内部的变化对其他对象而言是不可见的,对象对它自己的行为负责,其他对象或者用户都不关心它的内部实现,封装使得对象之间的耦合变松散,对象之间只通过暴露的 API 接口来通信。

  • 利用闭包来实现方法, 属性私有化.
  • 利用原型链来实现对象的公有属性和方法
  • 创建对象的安全模式来解决没有new 对象的误操作. 如果没有new对象, 构造函数中的this就指向了window
1
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
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
var Book = (function () {
// 静态私有变量
var bookNum = 0
// 静态私有方法
function checkBook(name) {}
// 返回构造函数
function _book(newId, newName, newPrice) {
// 创建对象的安全模式 防止有人误操作 没有执行new操作
if (this instanceof _book) {
// 私有变量
var name, price
// 私有方法
function checkID(id) {}
// 特权方法
this.getName = () => name
this.getPrice = () => price
this.setName = (n) => {
name = n
}
this.setPrice = (n) => {
price = n
}
// 公有属性
this.id = newId
// 公有方法
this.copy = () => {}
bookNum++
if (bookNum > 100) {
throw new Error('我们仅出版100本书')
}
// 构造器
this.setName(newName)
this.setPrice(newPrice)
} else {
return new _book(newId, newName, newPrice)
}
}
_book.prototype = {
// 公有属性
isJsBook: false,
// 公有方法
display: () => {},
}
return _book
})()

继承

继承指的是可以让某个类型的对象获得另一个类型的对象的属性和方法. JS 中实现继承的方式有多种,详见JS中的继承.