面向对象的语言由三大特性:
- 封装
- 继承
- 多态
JavaScript是一种面向对象的语言,上面已经介绍了JavaScript的封装,下面来介绍继承。
继承,在javascript中表示子类继承了父类的属性和方法。下面用代码来做更深一步的解释:
JavaScript实现继承的方式有很多种,首先先介绍利用原型链实现继承。
1.原型链实现继承
//定义一个父类
function Parent(){
this.pv="parent";
}
Parent.prototype.showParent=function(){
alert(this.pv);
}
//定义一个子类
function Son(){
this.sv="son";
}
//使用原型链来实现继承
Son.prototype=new Parent();
Son.prototype.showSon=function(){
alert(this.sv);
}
var s1 = new Son();
s1.showParent();
s1.showSon();
此时的内存模型图为:
注:子类自身的方法,要在重置原型之后定义,不然子类在重置原型之前定义,子类的方法会定义在子类的原始原型中,当子类重置原型后,子类指向重置原型后内存新开辟的一块内存,子类的原始原型会被释放,此时就找不到子类的方法了。
2.父类方法的覆盖(重写)
当子类继承父类后,子类如果认为父类的方法不能满足自己或者不太满意父类的方法,可以使用与父类同名的方法来覆盖(也叫重写)父类方法。
function Parent(){
this.pv="father";
}
Parent.prototype.parentSay=function(){
console.log(this.pv);
}
function Son(){
this.sv="son";
}
//子类的原型指向父类的对象
Son.prototype=new Parent();
Son.prototype.sonSay=function(){
console.log(this.sv);
}
var s1=new Son();
s1.sonSay();
s1.parentSay();//父类的方法被子类继承
Son.prototype.parentSay=function(){
console.log("我是被重写了...");
}
s1.parentSay();//父类的方法被重写
重写的内存模块图:
注:JavaScript中存在重写,但是没有重载。
原型链继承的缺陷:
1.无法从子类中调用父类的构造函数(当new Son()时,不能够包含new Parent()),这样就没有办法把子类中属性赋值给父类。
2.父类中属性是在子类的原型中的,这违背了前面我们所讲的封装的理念(属性在对象中,方法在原型中),会出现前面值混淆问题。
3.基于伪装实现继承
在前面我们学习了call和apply方法,这两个方法可以使用:函数名.call(上下文,参数列表),或:函数名.apply(上下文,参数数组)的方式来调用函数,这样我们可以通过第一个参数上下文来改变调用函数的对象。基于这两个方法,我们就可以实现一个基于伪装的继承。
代码实现如下:
//定义一个父类
function Parent(){
this.pv="parent";
}
//定义一个子类
function Son(){
this.sv="son";
Parent.call(this);//注意:此时的this代表上下文,指的是Son对象,那么就是Son对象调用Parent函数。
}
var s1 = new Son();
alert(s1.pv);
在子类中的this指的是子类实例化后的对象本身,当我们在子类中使用call方法调用父类后,就相当于父类的构造方法绑定到了子类的对象身上,就这样伪装了子类可以使用父类的构造方法,完成了继承。
子类初始化父类属性
基于原型链的继承我们说了缺陷就是无法实现子类去调用父类的构造函数,这样就无法修改父类的属性,但是基于伪装的完美解决了这个问题,代码演示如下:
//定义一个父类
function Parent(name){
this.name=name;
}
//定义一个子类
function Son(name,age){
this.age=age;
Parent.call(this,name);
}
var s1=new Son("liyanan",13);
var s2=new Son("lyn",15);
console.log(s1.name+","+s1.age);
console.log(s2.name+","+s2.age);
注:基于伪装的继承解决了原型链方法继承不到父类构造方法的缺陷,但是它也存在了问题:
1.由于使用伪造的方式继承,子类的原型不会指向父类,所以父类写在原型中的方法不会被子类继承,所以子类调用不到父类的方法。
2.解决的办法就是将父类的方法放到子类中来,但是这样又违背了封装的理念。
4.实现继承的终极方案–基于组合实现继承
基于组合的继承就是:属性的方法基于伪装的方式实现,而方法的继承基于原型链的方式继承。
代码实现如下:
function Parent(name){
this.name=name;
this.friends=["jc","dlj"];
}
Parent.prototype.parentSay=function(){
alert(this.name+"----->"+this.friends);
}
//定义一个子类
function Son(name,age){
this.age=age;
Parent.apply(this,[name]);
}
//使用原型链来实现继承
Son.prototype=new Parent();
Son.prototype.sonSay=function(){
alert(this.name+"------>"+this.age);
}
var s1=new Son("liyanan",13);
s1.friends.push("lh");
s1.parentSay();
s1.sonSay();
var s2=new Son("lyn",15);
s2.parentSay();
s2.sonSay();