继承
继承是面向对象中一个核心的概念。其他正统面向对象语言都会用两种方式实现继承:
一个是接口实现,一个是继承。
而ECMAScript只支持继承,不支持接口实现,而实现继承的方式依靠原型链完成。
//继承,通过原型链实现 function Box(){ //被继承的函数叫超类型(父类,基类) this.name="link" } function Dest(){//继承的函数叫子类型(子类,派生类) this.age=25; } function Table(){ this.level="dfsfsdf"; } //通过原型链继承,超类型实例化后的对象实例,赋值给予类型的原型属性 //new Box()会将Box构造里的信息和原型里的信息都提交给Dest Dest.prototype=new Box(); Table.prototype=new Dest(); var dest=new Dest(); alert(dest.age) alert(dest.name) var table=new Dest(); alert(table.age) alert(table.name)
如果要实例化Dest,那么Box实例中的name="link",原型中添加相同的属性name="xiao",如果有Box有就是link,没有就是xiao
//继承,通过原型链实现 function Box(){ //被继承的函数叫超类型(父类,基类) //this.name="link" } Box.prototype.name="xiao"; function Dest(){//继承的函数叫子类型(子类,派生类) this.age=25; } //通过原型链继承,超类型实例化后的对象实例,赋值给予类型的原型属性 //new Box()会将Box构造里的信息和原型里的信息都提交给Dest Dest.prototype=new Box(); var dest=new Dest(); alert(dest.age) alert(dest.name) //xiao
ps:以上原型链继承还少一环,那就是Object,所有的构造函数都继承自Object。而继承object是自动完成的,并不需要程序员手动继承。
//经过继承后的实例,他们的从属关系会是什么? alert(table instanceof Object); //true alert(dest instanceof Table); //false,dest是Table的超类 alert(table instanceof Dest); //true alert(table instanceof Box); //true
在javascript里,被继承的函数称为超类型(父类,基类),继承的函数称为子类型(子类,派生类)。继承也有之前的问题比如字面量重写原型会中断关系,使用引用类型的原型,并且子类型还无法给超类型传递参数。
为了解决共享和超类型无法传参的问题,我们采用了一种叫借用构造函数的技术,或者成为对象冒充(伪造对象,经典继承)的技术来解决这两种问题。
//使用对象冒充 function Box(name,age){ this.name=name; this.age=age; this.family=['哥哥','妹妹','弟弟'] //引用类型,放在构造里就不会共享。 } //Box.prototype.family='姐姐'; function Dest(name,age){ Box.call(this,name,age) //对象冒充,对象冒充只能继承构造里的信息 } var dest=new Dest('link',111); dest.family.push('姐姐'); alert(dest.name); alert(dest.family); var dest2=new Dest('html',111); alert(dest2.family);
借用构造函数虽然解决了刚才两种问题,但是没有原型,复用则无从谈起。所以,我们需要原型链+借用构造函数的模式,这种模式成为组合继承。
function Box(name,age){ this.name=name; this.age=age; this.family=['哥哥','妹妹','弟弟'] //引用类型,放在构造里就不会共享。 } Box.prototype.run=function(){ return this.name+this.age+"运行中..."; } //构造函数里的方法,放在构造里,每次实例化,都会分配一个内存地址,浪费,所以最好放到原型里,保证多次实例化只有一个地址。 function Dest(name,age){ Box.call(this,name,age) //对象冒充,对象冒充只能继承构造里的信息 } Dest.prototype=new Box(); //原型链继承 var dest=new Dest('link',111); alert(dest.run());
还有一种继承模式叫:原型式继承:这种继承借助原型并基于已有的对象创建对象,同时还不必因此创建自定义类型。
我们学习过:
1.原型链继承,2.借用构造函数继承(对象冒充继承)3.组合继承(结合前俩种)
4.原型式继承
//4.原型式继承 //临时中转函数 function obj(o){ //o表示将要传递进入的一个对象 function F(){} //F构造是一个临时新建的对象,用来存储传递过来的对象 F.prototype=o; //将o对象实例赋值给F构造的原型对象 return new F(); //最后返回这个得到传递过来对象的对象实例 } //F.prototype=o 其实就是相当于Dest.prototype=new Box(); //这个字面量的声明方式,相当于var box=new Box(); var box={ name:'link', age:23, family:['哥哥','妹妹','弟弟'] } //box1等于new F(); var box1=obj(box); alert(box1.name); alert(box1.family); box.family.push('姐姐'); alert(box1.family); var box2=obj(box); alert(box2.family);
5.寄生式继承=原型式+工厂模式
//临时中转函数 function obj(o){ function F(){} F.prototype=o; return new F(); } //寄生函数 function create(o){ var f=obj(o); return f; } var box={ name:'link', age:23, family:['哥哥','妹妹','弟弟'] } var box1=new create(box); alert(box1.name);
//临时中转函数 function obj(o){ function F(){} F.prototype=o; return new F(); } //寄生函数 function create(box,dest){ var f=obj(box.prototype); f.constructor=dest; //指向了Dest,没有是话指向Box dest.prototype=f; } function Box(name,age){ this.name=name; this.age=age; } Box.prototype.run=function(){ return this.name+this.age+"运行中。。。"; } function Dest(name,age){ Box.call(this,name,age) } //通过寄生组合继承来实现继承 create(Box,Dest); //这句话用来替代Dest.prototype=new Box(); var dest=new Dest('link',23); alert(dest.run()); alert(dest.constructor);