javascript--继承
在javascript--创建对象中,我们知道了几种创建对象的方法。但是在编码过程中,每个对象之间并不是孤立的,有很多子对象都是依据父对象而创建的。这里就引出了javascript一个重要的概念--继承!下面将介绍几种常见的继承方式。
一、借用构造函数
函数不过是在特定环境中执行代码的对象,因此通过使用call()、apply()方法在新创建的对象上执行构造函数。
function Cloth(cate){ this.cate=cate; } function Shirt(color,price){ Cloth.call(this,"寸衫"); this.color=color; this.price=price; } var shirt1=new Shirt("white",100); alert(shirt1.cate); //寸衫 alert(shirt1.color); //white
且shirt1实例的属性都在实例对象上:
alert(shirt1.hasOwnProperty("cate")); //true
构造函数模式中,方法都在构造函数中定义,因此函数无法复用。且在父对象原型中定义的属性、方法,在子对象中是不可见。实践中很少单独借用构造函数的技术来实现继承。
二、原型链
原型链的基本思想是利用原型让一个引用类型继承另一个引用类型的属性和方法。简单回顾下构造函数、原型和实例间的关系:每个构造函数都有一个原型对象(prototype),原型对象都包含一个指向构造函数的指针(constructor),而实例都包含一个指向原型对象的内部指针!
function Cloth(cate){ this.cate=cate; this.style=["pop","classical","fashion"]; } Cloth.prototype={ sayStyle:function(){ alert(this.style); }, }
我们在父对象中定义了实例属性和原型方法。
function Shirt(color,price){ this.color=color; this.price=price; } Shirt.prototype=new Cloth(); Shirt.prototype.constructor=Shirt; Shirt.prototype.sayPrice=function(){ alert(this.price); }
再定义子对象的实例属性,将父对象的实例作为子对象原型对象的值。且将子对象原型的constructor属性重新赋值,因为原来Shirt.prototype被重写了。
var shirt1=new Shirt("white","188"); shirt1.style.push("brief"); shirt1.sayStyle(); //pop,classical,fashion,brief shirt1.sayPrice(); //188 var shirt2=new Shirt("black","388"); shirt2.sayStyle(); //pop,classical,fashion,brief shirt2.sayPrice(); //388 alert(shirt1.hasOwnProperty("cate")); //false
从上面例子可看出,子对象通过原型可以继承父对象实例、原型属性和方法,但是继承来的所有属性、方法都在子对象Shirt的原型对象中。
问题:只要改变属性值,其所有实例都发生改变;不能给父对象构造函数传递参数。因此,实践中很少单独使用原型链!
三、组合继承
组合继承即将借用构造函数和原型链两种方式结合的一种模式,发挥两者之长。使用原型链实现原型属性和方法的继承,而实现函数的复用;通过借用构造函数实现对实例属性和方法的继承,使每个实例都有其特异性且能为每个实例传入参数!
function Cloth(cate){ this.cate=cate; this.style=["pop","classical","fashion"]; } Cloth.prototype={ sayStyle:function(){ alert(this.style); }
} function Shirt(cate,color,price){ Cloth.call(this,cate); //继承实例对象 this.color=color; this.price=price; } Shirt.prototype=new Cloth(); //继承原型对象 Shirt.prototype.constructor=Shirt; Shirt.prototype.sayPrice=function(){ alert(this.price); }
var shirt1=new Shirt("shirt","white","188"); shirt1.style.push("brief"); shirt1.sayStyle(); //pop,classical,fashion,brief alert(shirt1.cate); //shirt var shirt2=new Shirt("jacket","black","388");
shirt2.sayStyle(); //pop,classical,fashion alert(shirt2.cate);; //jacket alert(shirt1.hasOwnProperty("cate")); //true
因为原型链模式是将父构造函数的实例、原型属性和方法都赋值给子构造函数的原型对象;而借用构造函数模式则是子构造函数仅继承父构造函数的实例属性、方法。
组合继承模式是两种模式共同作用的结果,子构造函数中实例属性和原型属性中都含有一份父构造函数中的实例属性,而实例属性屏蔽掉了同名原型属性。
组合继承模式避免两者缺陷,融合优点,成为javascript中最常见的继承模式!
四、寄生组合继承
前面说过组合继承固然能达到继承所需的条件,但是会调用两次父构造函数,且子构造函数中实例属性和原型属性中都含有一份父构造函数中的实例属性。而寄生组合继承真正达到了子对象分别继承父对象的实例、原型的目的。
先定义两个函数:
function object(o){ function F(){}; F.prototype=o; return new F(); } function inheritPrototype(Child,Parent){ var prototype=object(Parent.prototype); prototype.constructor=Child; Child.prototype=prototype; }
object函数作用为子对象通过原型继承基础对象并返回;inheritPrototype函数作用返回父构造函数的原型对象,并赋值给子构造函数的原型对象,且不改变子构造函数原型的constructor属性。
重写组合继承例子:
function Cloth(cate){ this.cate=cate; this.style=["pop","classical","fashion"]; } Cloth.prototype={ sayStyle:function(){ alert(this.style); } } function Shirt(cate,color,price){ Cloth.call(this,cate); //继承实例对象 this.color=color; this.price=price; } inheritPrototype(Shirt,Cloth) Shirt.prototype.sayPrice=function(){ alert(this.price); }
var shirt1=new Shirt("shirt","white","188"); shirt1.style.push("brief"); shirt1.sayStyle(); //pop,classical,fashion,brief alert(shirt1.cate); //shirt var shirt2=new Shirt("jacket","black","388"); shirt2.sayStyle(); //pop,classical,fashion alert(shirt2.cate);; //jacket alert(shirt1.hasOwnProperty("cate")); //true
寄生组合模式的高效体现在值调用一次父构造函数,且不创建多余属性。因此,此模式是引用类型最理想的继承模式!