JavaScript是一门面向对象的语言。在JavaScript中有一句很经典的话,万物皆对象。既然是面向对象的,那就有面向对象的三大特征:封装、继承、多态。这里讲的是JavaScript的继承,其他两个容后再讲。
JavaScript的继承和C++的继承不大一样,C++的继承是基于类的,而JavaScript的继承是基于原型的。
现在问题来了。
原型是什么?原型我们可以参照C++里的类,同样的保存了对象的属性和方法。例如我们写一个简单的对象
复制代码 代码如下:
function Animal(name) {
this.name = name;
}
Animal.prototype.setName = function(name) {
this.name = name;
}
var animal = new Animal("wangwang");
我们可以看到,这就是一个对象Animal,该对象有个属性name,有个方法setName。要注意,一旦修改prototype,比如增加某个方法,则该对象所有实例将同享这个方法。例如
复制代码 代码如下:
function Animal(name) {
this.name = name;
}
var animal = new Animal("wangwang");
这时animal只有name属性。如果我们加上一句,
复制代码 代码如下:
Animal.prototype.setName = function(name) {
this.name = name;
}
这时animal也会有setName方法。
继承本复制——从空的对象开始我们知道,JS的基本类型中,有一种叫做object,而它的最基本实例就是空的对象,即直接调用new Object()生成的实例,或者是用字面量{ }来声明。空的对象是“干净的对象”,只有预定义的属性和方法,而其他所有对象都是继承自空对象,因此所有的对象都拥有这些预定义的 属性与方法。原型其实也是一个对象实例。原型的含义是指:如果构造器有一个原型对象A,则由该构造器创建的实例都必然复制自A。由于实例复制自对象A,所以实例必然继承了A的所有属性、方法和其他性质。那么,复制又是怎么实现的呢?方法一:构造复制每构造一个实例,都从原型中复制出一个实例来,新的实例与原型占用了相同的内存空间。这虽然使得obj1、obj2与它们的原型“完全一致”,但也非常不经济——内存空间的消耗会急速增加。如图:
方法二:写时复制这种策略来自于一致欺骗系统的技术:写时复制。这种欺骗的典型示例就是操作系统中的动态链接库(DDL),它的内存区总是写时复制的。如图:
我们只要在系统中指明obj1和obj2等同于它们的原型,这样在读取的时候,只需要顺着指示去读原型即可。当需要写对象(例如obj2)的属性时,我们就复制一个原型的映像出来,并使以后的操作指向该映像即可。如图:
这种方式的优点是我们在创建实例和读属性的时候不需要大量内存开销,只在第一次写的时候会用一些代码来分配内存,并带来一些代码和内存上的开销。但此后就不再有这种开销了,因为访问映像和访问原型的效率是一致的。不过,对于经常进行写操作的系统来说,这种方法并不比上一种方法经济。方法三:读遍历这种方法把复制的粒度从原型变成了成员。这种方法的特点是:仅当写某个实例的成员,将成员的信息复制到实例映像中。当写对象属性时,例如(obj2.value=10)时,会产生一个名为value的属性值,放在obj2对象的成员列表中。看图:
可以发现,obj2仍然是一个指向原型的引用,在操作过程中也没有与原型相同大小的对象实例创建出来。这样,写操作并不导致大量的内存分配,因此内存的使用上就显得经济了。不同的是,obj2(以及所有的对象实例)需要维护一张成员列表。这个成员列表遵循两条规则:保证在读取时首先被访问到如果在对象中没有指定属性,则尝试遍历对象的整个原型链,直到原型为空或或找到该属性。原型链后面会讲。显然,三种方法中,读遍历是性能最优的。所以,JavaScript的原型继承是读遍历的。constructor熟悉C++的人看完最上面的对象的代码,肯定会疑惑。没有class关键字还好理解,毕竟有function关键字,关键字不一样而已。但是,构造函数呢?实际上,JavaScript也是有类似的构造函数的,只不过叫做构造器。在使用new运算符的时候,其实已经调用了构造器,并将this绑定为对象。例如,我们用以下的代码
复制代码 代码如下:
var animal = Animal("wangwang");
animal将是undefined。有人会说,没有返回值当然是undefined。那如果将Animal的对象定义改一下:
复制代码 代码如下:
function Animal(name) {
this.name = name;
return this;
}