文章目录

JavaScript 语言是通过一种叫做 原型(prototype)的方式来实现面向对象编程的。下面就来讨论 基于类的(class-based)面向对象和 基于原型的 (prototype-based) 面向对象这两种方式在构造客观世界的方式上的差别。

在基于类的面向对象方式中,对象(object)依靠 类(class)来产生。而在基于原型的面向对象方式中,对象(object)则是依靠 构造器(constructor)利用 原型(prototype)构造出来的。举个客观世界的例子来说明二种方式认知的差异。例如工厂造一辆车,一方面,工人必须参照一张工程图纸,设计规定这辆车应该如何制造。这里的工程图纸就好比是语言中的 类 (class),而车就是按照这个 类(class)制造出来的;另一方面,工人和机器 ( 相当于 constructor) 利用各种零部件如发动机,轮胎,方向盘 ( 相当于 prototype 的各个属性 ) 将汽车构造出来。

首先,客观世界中的对象的产生都是其它实物对象构造的结果,而抽象的“图纸”是不能产生“汽车”的,也就是说,类是一个抽象概念而并非实体,而对象的产生是一个实体的产生;
其次,按照一切事物皆对象这个最基本的面向对象的法则来看,类 (class) 本身并不是一个对象,然而原型方式中的构造器 (constructor) 和原型 (prototype) 本身也是其他对象通过原型方式构造出来的对象。

最基本的面向对象
ECMAScript 是一门彻底的面向对象的编程语言(参考资源),JavaScript 是其中的一个变种 (variant)。它提供了 6 种基本数据类型,即 Boolean、Number、String、Null、Undefined、Object。为了实现面向对象,ECMAScript设计出了一种非常成功的数据结构 – JSON(JavaScript Object Notation), 这一经典结构已经可以脱离语言而成为一种广泛应用的数据交互格式 (参考资源)。
应该说,具有基本数据类型和 JSON 构造语法的 ECMAScript 已经基本可以实现面向对象的编程了。开发者可以随意地用 字面式声明(literal notation)方式来构造一个对象,并对其不存在的属性直接赋值,或者用 delete 将属性删除 ( 注:JS 中的 delete 关键字用于删除对象属性,经常被误作为 C++ 中的 delete,而后者是用于释放不再使用的对象 ),如 程序清单 2。

清单 2. 字面式 (literal notation) 对象声明

1
2
3
4
5
6
7
8
9
10
var person = {
name: “张三”,
age: 26,
gender: “男”,
eat: function( stuff ) {
alert( “我在吃” + stuff );
}
};
person.height = 176;
delete person[ “age” ];

大部分初学者或者对 JS 应用没有太高要求的开发者也基本上只用到 ECMAScript 定义的这一部分内容,就能满足基本的开发需求。然而,这样的代码复用性非常弱,与其他实现了继承、派生、多态等等的类式面向对象的强类型语言比较起来显得有些干瘪,不能满足复杂的 JS 应用开发。所以 ECMAScript 引入原型来解决对象继承问题。

使用函数构造器构造对象
除了 字面式声明(literal notation)方式之外,ECMAScript 允许通过 构造器(constructor)创建对象。每个构造器实际上是一个 函数(function) 对象, 该函数对象含有一个“prototype”属性用于实现 基于原型的继承(prototype-based inheritance)和 共享属性(shared properties)。对象可以由“new 关键字 + 构造器调用”的方式来创建,如 程序清单 3:

清单 3. 使用构造器 (constructor) 创建对象

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

// 构造器 Person 本身是一个函数对象
function Person() {
// 此处可做一些初始化工作
}
// 它有一个名叫 prototype 的属性
Person.prototype = {
name: “张三”,
age: 26,
gender: “男”,
eat: function( stuff ) {
alert( “我在吃” + stuff );
}
}
// 使用 new 关键字构造对象
var p = new Person();
文章目录