JS原型链与几种继承方法的对比

继承的方式有两种:接口继承、实现继承
接口继承:即虚函数,父类只定义接口,不具体实现
子类继承接口,自己负责实现这个方法,运行时动态决定
调用哪个实现。
实现继承:父类实现的方法,子类可以直接调用

JS中只支持实现继承,以原型链来实现。

回顾一下构造函数、原型、实例的关系:
每个构造函数都有一个原型对象,
原型对象又都包含一个constructor指针指向构造函数,
实例包含一个_proto_内部属性指向原型对象。

继承的实现方法(父类型叫Parent,子类型叫Child)
原本Child.prototype = Child自己的原型对象,
现在如果将Child.prototype = Parent的实例,
那么Parent实例的属性以及Parent所指向的原型对象的方法,
也可以通过Child的实例访问到。

function Parent(name){
  this.name = name;
}
Parent.prototype.getParentName(){
  return this.name;
}

function Child(name){
  this.name = name;
}
Child.prototype = new Parent("parent");

Child.prototype.getChildName(){
  return this.name;
}

var child1 = new Child("child");
alert(child1.getChildName());	//child
alert(child1.getParentName());	//parent

getcParentName()在原型链里的搜索过程:
1、child1实例里有没有这个方法?没有
2、child1的原型对象中有没有?即Parent的实例中,有没有这个方法?没有
3、那Parent的原型对象中有没有这个方法?有,则执行这个方法

其实这并不是原型链的最顶端,所有引用类型都继承了Object,所有函数的默认的
prototype都指向Object的实例。
所以Parent.prototype = Object的实例,
也正是因为如此,所有的函数都可以使用
toString、valueOf、hasOwnProperty、isPrototypeOf等函数,它们都是
Object.prototype中的方法

原型链的问题:
1、子类无法独占父类的引用属性
父类的引用类型,比如一个数组,是单独一份,每个子类的实例访问到的都是同一份,
修改它会反映到所有子类的实例上。
2、无法在不影响其他子类对象的基础上,给父类的构造函数传参
子类继承父类时,“可以给父类的构造函数传参”,但是所有子类都会被影响,如下:
Child.prototype = new Parent("parent");
所有的Child的实例,其Parent的name都叫“parent”。

越讲越感觉JS这个继承,好鸡肋

为了克服以上2个问题,又引出了其他几种基于原型链的继承方法(好复杂= =|)


1、借用构造函数(伪造对象/经典继承)

function Parent(name){
    this.name = name;
}

function Child(){
    Parent.call(this,"parent");
    this.age = 24;
}

这个方法,就尼玛像c语言里面的宏一样,把父类的构造函数里执行的语句抠出来,
在子类里执行一遍,于是子类就会有一个name属性,值是“parent”。
缺点:方法都在构造函数里,不可见,无法复用

2、组合继承(伪经典继承)

function Parent(name){
    this.name = name;
    this.colors = ["blue","yellow","green"];
}

function Child(name,age){
    Parent.call(this,name);
    this.age = age;
}

Child.prototype = new Parent();
Child.prototype.sayAge = function(){
    alert(this.age);
}

可以看出是结合了上述两种方法,
在原型链的基础上,借用构造函数解决了上述两种问题,
使得子类可以给父类传参,又保证了每个实例拥有自己的属性
这种方法比较常用

3、原型式继承

function object(o){
    function F(){};
    F.prototype = o;
    return new F();
}

这种继承没有构造函数,返回一个对象,这个对象的prototype
指向实例o。
ES5新增了Object.create()方法,规范了原型式继承。
这种方法和原型链方法类似,但是更精简一些,用于在已有一个
对象的基础上,要构建另一个与之相似的对象,可以新增方法,
或者覆盖原对象的属性。不过引用类型还是共享的

4、寄生式继承

function createAnother(o){
  var clone = object(o);
      clone.sayName = function(){
      alert("wzk");
      };
  return clone;
}

传入一个对象,并复制一个新对象,具有原对象的所有属性和方法,
并在函数内部添加了自己的sayName方法。

5、寄生组合式继承

回头看组合继承模式,Parent构造函数被调用了两次(红色)

function Parent(name){
    this.name = name;
    this.colors = ["blue","yellow","green"];
}

function Child(name,age){
    Parent.call(this,name);
    this.age = age;
}

Child.prototype = new Parent();
Child.prototype.sayAge = function(){
    alert(this.age);
}

第一次调用构造函数,子类取得父类的属性。

第二次调用构造函数,只是想取Parent的原型对象里的

方法,可改写成如下

function inheritPrototype(child,parent){
    var prototype = object(parent.prototype);
    prototype.constructor = child;
    child.prototype = prototype;
}

替代后的代码为:

function Parent(name){
    this.name = name;
    this.colors = ["blue","yellow","green"];
}

function Child(name,age){
    Parent.call(this,name);
    this.age = age;
}

//Child.prototype = new Parent();
inheritPrototype(Parent,Child);
Child.prototype.sayAge = function(){
  alert(this.age);
}

  

posted @ 2016-01-16 17:31  olaf  阅读(981)  评论(0编辑  收藏  举报