本文共 2794 字,大约阅读时间需要 9 分钟。
面向对象语言有两种继承方式:接口继承(只继承方法名);实现继承(继承实际的方法)。但在ECMAScript中,函数名没多大含义,只是函数体的引用而已,因此,ECMAScript无法实现接口继承,只支持实现继承。实现继承,主要是依靠原型链来完成的。 一、原型链
1.原型链的基本思想是:利用原型让一个引用类型继承另一个引用类型的属性和方法。 2.构造函数、原型、实例之间的关系 (1)每个构造函数都有一个原型对象,Func.prototype指向了这个原型对象。 (2)原型对象都包含一个指向构造函数的指针,Func.prototype.constructor等于Func,可以理解为prototype和constructor是两个方向相反的指针,在构造函数和原型之间搭建起桥梁。 (3)每个实例,都包含一个指向原型对象的内部指针__proto__,当然这个对开发人员是不可见的。 (4)如果把构造函数A的原型,设成构造函数B的一个实例,那么构造函数A的原型里就包含了指向另一个原型的指针,从而形成了原型链。这样就实现了继承。使用对象的属性或方法时,解析器先搜索实例内部,找不到就搜索实例的原型内部,再找不到就搜索实例的原型的构造函数的原型内部,一级一级向上查找。 -
- function Human(){
-
- }
- Human.prototype.getSex=function(){ return this.sex;}
- Human.prototype.setSex=function(v){ this.sex=v;}
-
- function Male(){
- this.setSex("male");
- }
- Male.prototype=new Human();
- Male.prototype.getAge=function(){ return this.age;}
- Male.prototype.setAge=function(v){ this.age=v;}
-
-
- var tom=new Male();
- tom.setAge(20);
-
- alert(tom.getSex());
- alert(tom.getAge());
- alert(tom instanceof Male);
- alert(tom instanceof Human);
- alert(tom instanceof Object);
- alert(tom.constructor == Male);
- alert(tom.constructor == Human);
(5)原型链的最顶端是Object,这就是为什么所有引用类型instanceof Object都会返回true。换句话说,只要instanceof后面的构造函数在实例的原型链中,都会返回true。 (6)注意,由于继承的时候,原型被重写了,所以tom的constructor指像的不再是Male,而是Male.prototype的构造函数Human。 3.使用原型链做继承的问题。 (1)和原型模式创建对象一样,使用原型链做继承,也会遇到多个实例共享object(引用类型)属性的情况。这也就是为啥引用类型属性要放在构造函数内部。 (2)更严重一个问题是,使用原型链做继承时,必须用基类的实例去替换派生类的原型,产生基类的实例的过程是不能使用参数的,这就大大降低了继承的灵活性。 二、借用构造函数
1.为了解决原型链做继承遇到的第一个问题,出现了借用构造函数的方法。思想是把基类的构造函数当作普通函数执行一次,但执行的时候,必须把派生类的执行空间传过去。 -
- function Human(){
- this.sex="Please input";
- this.age=-1;
- }
-
- function Male(){
- Human.call(this);
- }
- Male.prototype=new Human();
-
- var tom=new Male();
- var jim=new Male();
- tom.sex="male";
- alert(tom.sex);
- alert(jim.sex);
2.当然,构造时不传递参数用处还是不大,幸好apply和call都是支持传参的。 -
- function Human(sex,age){
- this.sex=sex;
- this.age=age;
- }
-
- function Male(age){
- Human.apply(this,["male",age]);
- }
- Male.prototype=new Human();
-
- var tom=new Male(30);
- var jim=new Male(25);
- alert(tom.sex);
- alert(jim.age);
- alert(tom.age);
3.借用构造函数解决了引用类型属性的问题,但没有解决方法的问题。 三、组合继承(伪经典继承)
1.这种继承是把原型链和借用构造函数结合起来。用借用构造函数处理属性,用原型链处理方法。其实就相当于创建对象时提到的“组合模式” -
- function Human(sex){
- this._sex=sex;
- }
- Human.prototype.getSex=function(){ return this._sex;}
- Human.prototype.setSex=function(v){ this._sex=v;}
-
- function Male(name,age){
- Human.call(this,"male");
- this.name=name;
- this.age=age;
- }
- Male.prototype=new Human();
- Male.prototype.sayHello=function(){
- alert("Hello, my name is "+this.name+", I'm "+this.age+" years old, I'm a "+(this.getSex()=="male"?"boy":"girl")+".");
- }
-
- var tom=new Male("tom",15);
- var jan=new Male("jan",13);
- jan.setSex("female");
- tom.sayHello();
- jan.sayHello();
2.这是目前最常用,也是解决最完美的继承写法了,没有之一。 转载地址:http://pwkxi.baihongyu.com/