javascirpt的原型继承原理
要理解JavaScript到底是怎么实现面向对象、继续以及多态的原理,就要追溯到它本身的语言特点说起,JavaScript和Java等后端语言不同,它没有强制性地要求声明时就要知道某个数据的类型,也就是说,JavaScript是一门灵活多变的动态语言,也正是因为这样的特点,所以才让它具有原型继承的一系列优点,
从原型设计模式中,我们可以非常清楚的知道,编程范型这种设计是,有多么的好,实际上基于原型进行继承的语言,也不止有JavaScript一种,在很早之前就有了,比喻:Self 语言和 Smalltalk 语言,Io语言,
JavaScript就是参考了这几种语言的。下面我们以Io语言来举例:
Io 语言在 2002 年由 Steve Dekorte 发明。可以从http://iolanguage.com下载到 Io 语言的解释器,
安装好之后打开 Io 解释器,输入经典的“Hello World”程序。解释器打印出了 Hello World 的字
符串,这说明我们已经可以使用 Io 语言来编写一些小程序了,如图 1-1 所示。
图 1-1
作为一门基于原型的语言,Io 中同样没有类的概念,每一个对象都是基于另外一个对象的
克隆。
就像吸血鬼的故事里必然有一个吸血鬼祖先一样,既然每个对象都是由其他对象克隆而来
的,那么我们猜测 Io 语言本身至少要提供一个根对象,其他对象都发源于这个根对象。这个猜
测是正确的,在 Io 中,根对象名为 Object。
这一节我们依然拿动物世界的例子来讲解 Io 语言。在下面的代码中,通过克隆根对象 Object,
就可以得到另外一个对象 Animal。虽然 Animal 是以大写开头的,但是记住 Io 中没有类,Animal
跟所有的数据一样都是对象。
Animal := Object clone // 克隆动物对象
现在通过克隆根对象 Object 得到了一个新的 Animal 对象,所以 Object 就被称为 Animal 的原
型。目前 Animal 对象和它的原型 Object 对象一模一样,还没有任何属于它自己方法和能力。我
们假设在 Io 的世界里,所有的动物都会发出叫声,那么现在就给 Animal 对象添加 makeSound 方法
吧。代码如下:
Animal makeSound := method( "animal makeSound " print );
好了,现在所有的动物都能够发出叫声了,那么再来继续创建一个 Dog 对象。显而易见,Animal
对象可以作为 Dog 对象的原型,Dog 对象从 Animal 对象克隆而来:
Dog := Animal clone
可以确定,Dog 一定懂得怎么吃食物,所以接下来给 Dog 对象添加 eat 方法:
Dog eat = method( "dog eat " print );
现在已经完成了整个动物世界的构建,通过一次次克隆,Io 的对象世界里不再只有形单影只
的根对象 Object,而是多了两个新的对象:Animal 对象和 Dog 对象。其中 Dog 的原型是 Animal,
Animal 对象的原型是 Object。最后我们来测试 Animal 对象和 Dog 对象的功能。
先尝试调用 Animal 的 makeSound 方法,可以看到,动物顺利发出了叫声:
Animal makeSound // 输出:animal makeSound
然后再调用 Dog 的 eat 方法,同样我们也看到了预期的结果:
Dog eat // 输出:dog eat
我们看到了如何在 Io 语言中从无到有地创建一些对象。跟使用“类”
的语言不一样的地方是,Io 语言中最初只有一个根对象 Object,其他所有的对象都克隆自另外一
个对象。如果 A 对象是从 B 对象克隆而来的,那么 B 对象就是 A 对象的原型。
在上一小节的例子中,Object 是 Animal 的原型,而 Animal 是 Dog 的原型,它们之间形成了一
条原型链。这个原型链是很有用处的,当我们尝试调用 Dog 对象的某个方法时,而它本身却没有
这个方法,那么 Dog 对象会把这个请求委托给它的原型 Animal 对象,如果 Animal 对象也没有这
个属性,那么请求会顺着原型链继续被委托给 Animal 对象的原型 Object 对象,这样一来便能得
到继承的效果,看起来就像 Animal 是 Dog 的“父类”,Object 是 Animal 的“父类”。
这个机制并不复杂,却非常强大,Io 和 JavaScript 一样,基于原型链的委托机制就是原型继
承的本质。
我们来进行一些测试。在 Io 的解释器中执行 Dog makeSound 时,Dog 对象并没有 makeSound 方
法,于是把请求委托给了它的原型 Animal 对象 ,而 Animal 对象是有 makeSound 方法的,所以该条
语句可以顺利得到输出
在原型继承方面,JavaScript 的实现原理和 Io 语言非常相似,
JavaScript 也同样遵守这些原型编程的基本规则。
所有的数据都是对象。
要得到一个对象,不是通过实例化类,而是找到一个对象作为原型并克隆它。
对象会记住它的原型。
如果对象无法响应某个请求,它会把这个请求委托给它自己的原型
1. 所有的数据都是对象
JavaScript 在设计的时候,模仿 Java 引入了两套类型机制:基本类型和对象类型。基本类型
包括 undefined、number、boolean、string、function、object。从现在看来,这并不是一个好的
想法。
按照 JavaScript 设计者的本意,除了 undefined 之外,一切都应是对象。为了实现这一目标,
number、boolean、string 这几种基本类型数据也可以通过“包装类”的方式变成对象类型数据来
处理。
我们不能说在 JavaScript 中所有的数据都是对象,但可以说绝大部分数据都是对象。那么相
信在 JavaScript 中也一定会有一个根对象存在,这些对象追根溯源都来源于这个根对象。
事实上,JavaScript 中的根对象是 Object.prototype 对象。Object.prototype 对象是一个空的
对象。我们在 JavaScript 遇到的每个对象,实际上都是从 Object.prototype 对象克隆而来的,
Object.prototype 对象就是它们的原型。比如下面的 obj1 对象和 obj2 对象:
var obj1 = new Object();
var obj2 = {};
可以利用 ECMAScript 5 提供的 Object.getPrototypeOf 来查看这两个对象的原型:
console.log( Object.getPrototypeOf( obj1 ) === Object.prototype ); // 输出:true
console.log( Object.getPrototypeOf( obj2 ) === Object.prototype ); // 输出:true
待续