本节主要讲构造函数继承

比如,现在有一个"人"对象的构造函数。

1
2
3
function People(){
  this.species = "人";
}

还有一个"男孩"对象的构造函数。

1
2
3
4
function Boy(name,age){
  this.name = name;
  this.age = age;
}

怎样才能使"男孩"继承"人"呢?

一、 构造函数绑定

第一种方法也是最简单的方法,使用call或apply方法,将父对象的构造函数绑定在子对象上,即在子对象构造函数中加一行:

1
2
3
4
5
6
7
function Boy(name,age){
  People.apply(this, arguments);
  this.name = name;
  this.age    = age;
}
var Boy1 = new Boy("小明","18");
alert(Boy1.species); // 人

二、 prototype模式

第二种方法更常见,使用prototype属性。
如果"男孩"的prototype对象,指向一个People的实例,那么所有"男孩"的实例,就能继承People了。

1
2
3
4
Boy.prototype = new People();
Boy.prototype.constructor = Boy;
var boy1 = new Boy("小明","18");
alert(boy1.species); // 人

代码的第一行,我们将Boy的prototype对象指向一个People的实例。

1
Boy.prototype = new People();

它相当于完全删除了prototype 对象原先的值,然后赋予一个新值。但是,第二行又是什么意思呢?

1
Boy.prototype.constructor = Boy;

原来,任何一个prototype对象都有一个constructor属性,指向它的构造函数。如果没有"Boy.prototype = new People();"这一行,
Boy.prototype.constructor是指向Boy的;加了这一行以后,Boy.prototype.constructor指向People。

1
alert(Boy.prototype.constructor == People); //true

更重要的是,每一个实例也有一个constructor属性,默认调用prototype对象的constructor属性。

1
alert(boy1.constructor == Boy.prototype.constructor); // true

因此,在运行"Boy.prototype = new People();"这一行之后,boy1.constructor也指向People!

1
alert(boy1.constructor == People); // true

这显然会导致继承链的紊乱(boy1明明是用构造函数Boy生成的),因此我们必须手动纠正,将Boy.prototype对象的constructor值改为Boy。这就是第二行的意思。

这是很重要的一点,编程时务必要遵守。下文都遵循这一点,即如果替换了prototype对象,

1
o.prototype = {};

那么,下一步必然是为新的prototype对象加上constructor属性,并将这个属性指回原来的构造函数。

1
o.prototype.constructor = o;