今天又看了一遍js的面向对象方面的知识,重点看了继承相关内容,已经记不得看了第几次这个内容,终于觉得自己好像懂了,特记录下来过程。
js面向对象继承分为两大类,主要分为对象继承和非对象继承(拷贝继承),这次主要谈对象继承。对象继承主要有两种:原型继承和对象冒充继承。
一、原型继承,将子类的原型引用父类的实例,从而达到将子类的原型与父类的原型和父类的构造函数关联起来的目的。
function Person(name){ this.name=name; } Person.prototype.sayName=function(){ alert(this.name) } function Student(age){ this.age=age; } Student.prototype=new Person(‘zhangsan‘);//子类构造函数指向父类实例 Student.prototype.constructor=Student;//上一步指向父类实例时候,构造函数也指向父类,此处还原 var s=new Student(20); alert(s.age); //20 s.sayName(); //zhangsan
原型继承可以有效的将父类的原型和父类的构造函数里的方法均复制给子类,但是由于子类的构造函数必须指向父类实例,因此父类的成员变量无法通过子类进行实例化,如上述例子,name只能为zhangsan,无法进行子类初始化时候设置。因此我们使用第二种方法来解决此问题。
二、对象冒充法:
function Person(name){ this.name=name; } Person.prototype.sayName=function(){ alert(this.name) } function Student(name,age){ Person.call(this,name);//此处使用call进行对象冒充,将name初始化 this.age=age; } var s=new Student(‘zhangsan‘,20); alert(s.name)//zhangsan alert(s.age); //20 s.sayName(); //error 不是一个方法
Person.call(this,name)对象冒充,本质是使用执行s.Person(name),将Student作为执行环境,因此this.name将赋值给Student的实例s,但是我们看到s.sayName()方法执行中报错,因此此方式无法将原型赋值给子类。
三、混合继承,结合了原型继承和对象冒充继承的优点,解决了两者的不足。
function Person(name){ this.name=name; } Person.prototype.sayName=function(){ alert(this.name) } function Student(name,age){ //对象冒充 Person.call(this,name);//此处使用call进行对象冒充,将name初始化 this.age=age; } //原型继承 Student.prototype=new Person();//子类构造函数指向父类实例,与直接原型继承不同,此处不需要传递参数 Student.prototype.constructor=Student;//上一步指向父类实例时候,构造函数也指向父类,此处还原 var s=new Student(‘zhangsan‘,20); alert(s.name) //zhangsan 属性成功继承 alert(s.age); //20 s.sayName(); //zhangsan 原型成功继承
混合继承完美的解决了原型和属性的继承,但是我们发现继承中父类的属性在对象冒充和原型继承中都会被引用,如果属性很多会造成大量重复引用工作,因此参考extjs的原型设计,对继承进行优化。
四、优化,设计思想核心为:
1、在extend方法,建立中转函数,将中转函数的原型指向父类原型,此时中转函数只有原型,无属性,对中转函数进行实例化,并赋值给子类,成功过滤掉父类的属性。
2、将父类的原型进行保留,可以对子类定义中的父类进行解耦,也可以在子类覆盖父类后通过引用再次调用父类方法
function extend(sub,sup){ //建立中转函数,使用原型继承 var F=new Function(); //将 F.prototype=sup.prototype; sub.prototype=new F(); sub.prototype.constructor=sub//还原构造器 //保留父类原型对象,方便解耦,也可以获取用来父类 sub.superClass=sup.prototype } function Person(name,age){ this.name=name; this.age=age; } Person.prototype.id=10; Person.prototype.sayName=function(){ alert("person") } function Stu(name,age,no){ //使用superClass进行解耦 Stu.superClass.constructor.call(this,name,age); this.no=no; } //Stu.prototype=new Person(); extend(Stu,Person); Stu.prototype.sayName=function(){ alert("student") } var s=new Stu(‘zhangsna‘,10,100); //s.sayName(); Stu.superClass.sayName.call(s);
通过深拷贝建立继承关系为jquery的继承实现方法,此次暂不讨论。