JavaScript面向对象

JavaScript 对象的特征

在我看来,不论我们使用什么样的编程语言,我们都先应该去理解对象的本质特征(参考Grandy Booch《面向对象分析与设计》)。总结来看,对象有如下几个特点。

  • 对象具有唯一标识性:即使完全相同的两个对象,也并非同一个对象。
  • 对象有状态:对象具有状态,同一对象可能处于不同状态之下。
  • 对象具有行为:即对象的状态,可能因为它的行为产生变迁。

 

对象具有唯一标识性

一般而言,各种语言的对象唯一标识性都是用内存地址来体现的, 对象具有唯一标识的内存地址,所以具有唯一的标识。

所以,JavaScript程序员都知道,任何不同的JavaScript对象其实是互不相等的,我们可以看下面的代码,o1和o2初看是两个一模一样的对象,但是打印出来的结果却是false。

关于对象的第二个和第三个特征“状态和行为”,不同语言会使用不同的术语来抽象描述它们,比如C++中称它们为“成员变量”和“成员函数”,Java中则称它们为“属性”和“方法”。

在 JavaScript中,将状态和行为统一抽象为“属性”,考虑到 JavaScript 中将函数设计成一种特殊对象(关于这点,我会在后面的文章中详细讲解,此处先不用细究),所以 JavaScript中的行为和状态都能用属性来抽象。

下面这段代码其实就展示了普通属性和函数作为属性的一个例子,其中o是对象,d是一个属性,而函数f也是一个属性,尽管写法不太相同,但是对JavaScript来说,d和f就是两个普通属性。

所以,总结一句话来看,在JavaScript中,对象的状态和行为其实都被抽象为了属性。如果你用过Java,一定不要觉得奇怪,尽管设计思路有一定差别,但是二者都很好地表现了对象的基本特征:标识性、状态和行为。

在实现了对象基本特征的基础上, 我认为,JavaScript中对象独有的特色是:对象具有高度的动态性,这是因为JavaScript赋予了使用者在运行时为对象添改状态和行为的能力。

下面这段代码就展示了运行时如何向一个对象添加属性,一开始我定义了一个对象o,定义完成之后,再添加它的属性b,这样操作是完全没问题的。

为了提高抽象能力,JavaScript的属性被设计成比别的语言更加复杂的形式,它提供了数据属性和访问器属性(getter/setter)两类。

 

JavaScript对象的两类属性

对JavaScript来说,属性并非只是简单的名称和值,JavaScript用一组特征(attribute)来描述属性(property)。

先来说第一类属性,数据属性。它比较接近于其它语言的属性概念。数据属性具有四个特征。

  • value:就是属性的值。
  • writable:决定属性能否被赋值。
  • enumerable:决定for in能否枚举该属性。
  • configurable:决定该属性能否被删除或者改变特征值。

在大多数情况下,我们只关心数据属性的值即可。

第二类属性是访问器(getter/setter)属性,它也有四个特征。

  • getter:函数或undefined,在取属性值时被调用。
  • setter:函数或undefined,在设置属性值时被调用。
  • enumerable:决定for in能否枚举该属性。
  • configurable:决定该属性能否被删除或者改变特征值。

访问器属性使得属性在读和写时执行代码,它允许使用者在写和读属性时,得到完全不同的值,它可以视为一种函数的语法糖。

 

JavaScript语言标准也已经明确说明,JavaScript是一门面向对象的语言,我想标准中能这样说,正是因为JavaScript的高度动态性的对象系统。

 

ES6 中的类

ES6中引入了class关键字,并且在标准中删除了所有[[class]]相关的私有属性描述,类的概念正式从属性升级成语言的基础设施,从此,基于类的编程方式成为了JavaScript的官方编程范式。

类的基本写法:

在现有的类语法中,getter/setter和method是兼容性最好的。

我们通过get/set关键字来创建getter,通过括号和大括号来创建方法,数据型成员最好写在构造器里面。

类的写法实际上也是由原型运行时来承载的,逻辑上JavaScript认为每个类是有共同原型的一组对象,类中定义的方法和属性则会被写在原型对象之上。

此外,最重要的是,类提供了继承能力。我们来看一下下面的代码。