参考自阮一峰老师的博文。
这篇文章延续上一篇,今天介绍一下继承。
现在有一个“动物“的构造函数:
function Animal(){ this.species = "动物"; }
还有一个猫对象的构造函数要让猫继承自动物这个对象:function Cat(name,color){ this.name = name; this.color = color; }
1.构造函数绑定
使用call或者apply,将父对象的构造函数绑定在子对象上
function Animal(){ this.species = "动物"; }
function Cat(name,color){ Animal.apply(this, arguments); this.name = name; this.color = color; } var cat1 = new Cat("大毛","黄色"); alert(cat1.species); // 动物
2.prototype
比如猫的prototype指向一个Animal的实例,那么所有猫的实例既可以继承自Animal了
function Animal(){ this.species = "动物"; }
Cat.prototype = new Animal(); Cat.prototype.constructor = Cat; var cat1 = new Cat("大毛","黄色"); alert(cat1.species); // 动物
我们将猫的prototype对象指向一个Animal实例
它相当于完全删除了prototype对象原先的值Cat.prototype=new Animal();
这句的意思就是,任何一个prototype对象都有一个constructor属性,指向它的构造函数。如果没有Cat.prototype=new Animal()这一句,cat的constructor是指向Cat的,但是加上这一句他就会指向Animal。Cat.prototype.constructor = Cat;
每一个实例也有一个constructor属性,默认调用prototype的constructor属性alert(Cat.prototype.constructor == Animal); //true
因此,在运行"Cat.prototype = new Animal();"这一行之后,cat1.constructor也指向Animalalert(cat1.constructor == Cat.prototype.constructor); // true
这显然会导致继承链的紊乱(cat1明明是用构造函数Cat生成的),因此我们必须手动纠正,将Cat.prototype对象的constructor值改为Cat。这就是第二行的意思。alert(cat1.constructor == Animal); // true
3.直接继承prototype
第三种方法是对第二种方法的改进。由于Animal对象中,不变的属性都可以直接写入Animal.prototype。所以,我们也可以让Cat()跳过 Animal(),直接继承Animal.prototype。
我们先将Animal对象改写:
然后,将Cat的prototype对象,然后指向Animal的prototype对象,这样就完成了继承。function Animal(){ } Animal.prototype.species = "动物";
与前一种方法相比,这样做的优点是效率比较高(不用执行和建立Animal的实例了),比较省内存。缺点是 Cat.prototype和Animal.prototype现在指向了同一个对象,那么任何对Cat.prototype的修改,都会反映到Animal.prototype。Cat.prototype = Animal.prototype; Cat.prototype.constructor = Cat; var cat1 = new Cat("大毛","黄色"); alert(cat1.species); // 动物
Cat.prototype.constructor = Cat;这一句实际上把Animal.prototype对象的constructor属性也改掉了!
4.利用空对象作为中介
由于"直接继承prototype"存在上述的缺点,所以就有第四种方法,利用一个空对象作为中介。F是空对象,所以几乎不占内存。这时,修改Cat的prototype对象,就不会影响到Animal的prototype对象。var F = function(){}; F.prototype = Animal.prototype; Cat.prototype = new F(); Cat.prototype.constructor = Cat;
我们将上面的方法,封装成一个函数,便于使用。alert(Animal.prototype.constructor); // Animal
使用:function extend(Child, Parent) { var F = function(){}; F.prototype = Parent.prototype; Child.prototype = new F(); Child.prototype.constructor = Child; Child.uber = Parent.prototype; }
extend(Cat,Animal); var cat1 = new Cat("大毛","黄色"); alert(cat1.species); // 动物
5.拷贝继承
上面是采用prototype对象,实现继承。我们也可以换一种思路,纯粹采用"拷贝"方法实现继承。简单说,如果把父对象的所有属性和方法,拷贝进子对象,不也能够实现继承吗?这样我们就有了第五种方法。
然后,再写一个函数,实现属性拷贝的目的。function Animal(){} Animal.prototype.species = "动物";
这个函数的作用,就是将父对象的prototype对象中的属性,一一拷贝给Child对象的prototype对象。function extend2(Child, Parent) { var p = Parent.prototype; var c = Child.prototype; for (var i in p) { c[i] = p[i]; } c.uber = p; }
extend2(Cat, Animal); var cat1 = new Cat("大毛","黄色"); alert(cat1.species); // 动物