JavaScript面向对象有感

面向对象程序设计(简称 OOP)是现在最流行的程序设计方法,JavaScript 的核心是支持面向对象的,Prototype 是 JavaScript 实现与管理继承的一种机制。最近在读《你不知道的 JavaScript》一书中了解到除了使用类实现外,还可以用一种“对象关联”(OLOO,objects linked to other objects)的编程风格

在 JavaScript 中定义一个类的传统方法是通过构造函数。可以这样写:

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
function Foo(name) {
this.name = name;
}

Foo.prototype.getName = function() {
console.log(`name:${this.name}`);
};

function Bar(name, age) {
Foo.call(this, name);

this.age = age;
}

Bar.prototype = Object.create(Foo.prototype);
Bar.prototype.constructor = Bar;

// 或者使用es6新增的setPrototypeOf方法
// Object.setPrototypeOf(Bar.prototype, Foo.prototype);

Bar.prototype.speak = function() {
console.log(`My name is ${this.name}, age ${this.age}`);
};

const b1 = new Bar('Zubin', 10);
const b2 = new Bar('Jake', 15);

b1.getName(); // name:Zubin
b1.speak(); // My name is Zubin, age 10
b2.getName(); // name:Jake
b2.speak(); //My name is Jake, age 15

ES6 提供了更接近传统语言的写法,引入了 Class(类)这个概念,作为对象的模板。通过 class 关键字,可以定义类。

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
class Foo {
constructor(name) {
this.name = name;
}

getName() {
console.log(`name:${this.name}`);
}
}

class Bar extends Foo {
constructor(name, age) {
super(name);
this.age = age;
}

speak() {
console.log(`My name is ${this.name}, age ${this.age}`);
}
}

const b1 = new Bar('Zubin', 10);
const b2 = new Bar('Jake', 15);

b1.getName(); // name:Zubin
b1.speak(); // My name is Zubin, age 10
b2.getName(); // name:Jake
b2.speak(); // My name is Jake, age 15

最后是《你不知道的 JavaScript》一书中作者提倡的一种 OLOO 的风格,使用 Object.create() 方法直接生成实例。

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
const Foo = {
init(name) {
this.name = name;
},
getName() {
console.log(`name:${this.name}`);
},
};

const Bar = Object.create(Foo);
Bar.init = function(name, age) {
this.name = name;
this.age = age;
};
Bar.speak = function() {
console.log(`My name is ${this.name}, age ${this.age}`);
};

const b1 = Object.create(Bar);
b1.init('Zubin', 10);

const b2 = Object.create(Bar);
b2.init('Jake', 15);

b1.getName(); // name:Zubin
b1.speak(); // My name is Zubin, age 10
b2.getName(); // name:Jake
b2.speak(); //My name is Jake, age 15

从书里的描述来看,作者的观点是 OLOO 总比 Class 的形式要好。如果你只读这本书,那么结论必然是我们不需要传统的 class 形式。个人认为 Class 未必就像书中所说的那样不堪,现实中有几点需要考虑:

  1. Class 在构建对象实例时使用约定成俗的 new 方法,风格更加统一,OLOO 写法中的 init 方法是自己定义的
1
2
3
4
5
6
// class
var f = new Foo(123, 'abc'); // 标准

//OLOO
var f = Object.create(Foo); // 需要两个步骤
f.init(123, 'abc'); // init 并不是 JS 中的标准函数,而是 Foo 中自定义的
  1. 基于 Class 的面向对象的写法是“主流”,毕竟在项目中代码不是你一个人看的。

  2. Class 对于大部分人(尤其是后端程序员)更容易接受。

Class 是基于 prototype 更为严格和易用的语法糖,目的是为了 改善 原来 prototype 丑和难用,但是 C++ 和 Java 中的类 跟 js 中的类不是一回事 它们的继承,封装,多态在 js 的类中是不存在的。当然利用 TypeScript 的 Interface 来实现“面向 Interface 编程”也不错的选择。