Skip to content

Latest commit

 

History

History
57 lines (32 loc) · 4.63 KB

class-prototype-oop.md

File metadata and controls

57 lines (32 loc) · 4.63 KB

JavaScript的面向对象编程实现方式

前面提到有两种不同的方式来实现面向对象编程的思想。而我们在学习编程的时候,类(class)是绝大多数人在接触面向对象编程时最先接收的核心概念之一,也是C++、Java、PHP、Python这些流行的语言中所共有的。而这是实现面向对象编程思想的一种方式,称为基于类的编程方式(class-based programming)。

除此之外,还有一种实现方式,叫作基于原型的编程方式(prototype-based programming)。第一个采用基于原型方式的语言是Self,这个语言是1980年代中期由David Ungar和Randall Smith所编写的。 JavaScript语言的面向对象实现就是参照了Self语言,从而吸取了基于原型的方式。

随便说一下,Self语言是Smalltalk语言的一个变种,而Smalltalk则被认为是基于类的面向对象编程实现的样本语言。再加上主流的编程语言基本上都是采用的基于类的方式,所以在JavaScript语言中,也有很多地方可以看到基于类方式中的影子,而这些都给我们带来了不少理解上的混乱。

基于类的编程方式(class-based programming)

在基于类的编程方式中,或者叫面向类的方式中,类是对一类对象的抽象。其核心概念就是:一个对象是类的一个具体实例。

在这种方式下,意味着两点:

  1. 对象的创建必须基于某个类。
  2. 继承是通过类的继承来实现的。

对于基于类的面向对象编程方式,就不再过多展开了。

在基于类的编程方式下,对象与类的关系是“is-a”的关系。

基于原型的编程方式(prototype-based programming)

在基于原型的编程方式中,只有对象的概念,没有类(class)的概念,关注的就是对象本身。每个对象可以有自己的原型对象,并且可以重用原型对象的属性和方法,看起来就好像自己的属性和方法一样,这就叫作继承。

不同于面向类的方式:

  1. 对象的创建不依赖于类,可以有多种方式创建对象,最简单的就是直接写在代码里。
  2. 继承是通过对象的继承来实现的
    • 对象从它的原型对象那里继承,而它的原型对象也可以有自己的原型对象;
    • 通过原型对象一级级链接起来,这就叫作原型链,这也是继承在这种方式下的实现;
    • 一个对象也可以没有原型对象,这表示该对象没有继承其它任何对象。

在对象继承的情况下,当访问一个对象的属性时:

  1. 如果当前对象本身就有该属性,就直接使用;
  2. 如果当前对象本身没有该属性
    1. 而且该对象也没有原型对象,就抛出错误;
    2. 如果该对象有原型对象,就在原型对象中查找该属性;
      1. 如果原型对象中也没有该属性,就在原型对象的原型对象中查找,一直到找到这个属性,或者直到没有原型对象(原型链的终点)为止。

这个基于原型的方式可以这样来理解:假设你某天到朋友家去做客,忽然发现朋友家的茶几上有一个小盒子,里面放着各种遥控器;而你经常为在家里找不着遥控器而烦恼,于是你决定DIY一个。于是,你回家就找来各种工具,照着朋友家的遥控器盒依葫芦画瓢地来了一个,这下可解决大问题了。半夜起来看奥运,再也不用到处找遥控器了。

在这个例子里,朋友家的遥控器盒就是原型对象,你自己做的那个遥控器盒就是新创建的对象。

在基于原型的编程方式下,对象与原型对象的关系是“has-a”的关系。

在基于原型的编程方式中,创建对象就类似于这个依葫芦画瓢的过程。假如,你觉得这个遥控器盒子有很多的人需要,干脆自己做了一个打印机(对,用3D打印机)来制造这个盒子;有人需要,就按一下钮就制造出一个新的盒子。这个打印机在基于原型的编程方式下,就叫构造函数(constructor)。

哇,构造函数,多么熟悉的名称。正是由于这个构造函数,与类方式里面的构造函数同名而且看起来挺像的,在学习JavaScript的OOP时给我们带来了很多麻烦。

对于这里的构造函数,也许叫作“构建器”更合适,其目的是根据已知的原型对象构造、并且创建出一个新的对象,就像这里的打印机。不过为了能以一致的语言进行交流,仍然维持构造函数的叫法。

在继续深入讲解构造函数之前,先看一下创建对象的几种方式。