OOP in JavaScript

对象和类是面向对象的基础, 封装, 继承和多态是面向对象编程(OOP)的三大特性.

JavaScript提供了对象却缺乏类, 他不能和Java等语言一样显式的定义一个类. 但是JavaScript函数功能非常灵活, 我们可以使用构造函数和原型对象来实现类.

对象和类的概念

对象是面向对象编程中非常重要的一个概念, 一个对象是一个东西(某个人或者某件事)的描述.
对象包含特征和行为, 用OOP的术语来说, 特征是对象的属性, 行为是对象的方法.

类相当于一个模板, 基于这个模板可以创建不同的具体对象.

在JavaScript中一切都是基于对象的, 原型也是对象, JavaScript的继承和重用也是通过原型实现的.

构造函数

我们可以使用new Array()创建一个数组, 使用new Object()创建一个对象. Array()Object()是JavaScript内置的两个构造函数, 尽管JavaScript没有提供类, 我们可以将Array和Object理解为”类”的概念.

构造函数也是函数, 定义构造函数和其它函数并没有语法上的区别. 唯一的区别就是构造函数的首字母应该大写, 这也是JavaScript的编程规范.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// 定义Person()构造函数, 我们可以将它理解为Person类
function Person() {
// 为Person类添加属性
this.name = 'Mary';
this.age = 18;
this.sex = 'male';
// 为Person类添加方法
this.say = function () {
console.log('believe in youself');
}
}

// 通过Person()构造函数创建实例对象
var foo = new Person();
var bar = new Person();

foo实例对象和bar实例对象都是通过Person构造函数创建出来的, 但是修改其中一个的实例对象的属性和方法并不会影响的另一个实例对象的属性和方法.

constructor属性

当创建一个对象时, 一个特殊的属性就被JavaScript自动分配给这个对象了, 这个属性就是constructor属性. 当访问某个对象的constructor属性时, 就会返回创建这个对象的构造函数.

1
2
3
4
5
6
7
8
9
10
11
12
13
function Person() {
this.name = 'Mary';
this.age = 18;
this.sex = 'male';
this.say = function () {
console.log('believe in youself');
}
}
var bingo = new Person();
console.log(bingo.constructor); // 返回构造函数Person

var foo = {};
console.log(foo.constructor); // 返回内置的构造函数Object

原型对象

在JavaScript中定义一个函数, 这个函数就会拥有prototype属性, 构造函数也不例外.

1
2
3
4
5
6
7
8
9
10
11
12
function Person() {
this.name = 'Mary';
this.age = 18;
this.sex = 'male';
this.say = function () {
console.log('believe in youself');
}
}
console.log(typeof Person.prototype); // object

var bingo = new Person();
console.log(bingo.prototype); // undefined

构造函数Person的prototype属性是一个对象, 他是属于函数的, 我们称这个属性为原型对象. 从Person类的角度出发, 可以理解为prototype属性是属于Person类的. 同时Person类的实例对象是没有prototype属性的, 上面代码中的bingo.prototypeundefined, 说明prototype属性是共享的.

既然实例对象的prototype属性是一个对象, 那么我们就可以给这个对象添加属性和方法.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
function Person(name) {
this.name = name;
this.say = function () {
console.log('believe in youself');
}
}

// 在构造函数Person的prototype属性上添加属性和方法
// 在Person.prototype属性上定义的属性和方法可以直接被Person类的实例对象使用
Person.prototype.hobby = 'swim';
Person.prototype.run = function () {
console.log(this.name + 'is run in the park');
}
var foo = new Person('Mary');
console.log(foo.run());

我们可以通过hasOwnPrototype方法查看对象是否包含某个属性或方法.

类的实现总结

  • JavaScript没有类,但是构造函数可以实现类
  • 按照JavaScript编程规范,构造函数的首字母应该大写
  • 在创建对象时,JavaScript为这个对象分配了一个constructor属性,constructor属性是对象构造函数的引用
  • 函数在定义时拥有了prototype属性,prototype属性也是一个对象
  • prototype属性上的属性和方法是共享的,定义在prototype属性的属性和方法可以被类的实例对象使用
  • 如果属性或者方法能够定义在prototype属性上,就不要定义在构造函数中,使用prototype可以减少内存开销

参考链接