javascript 继承方法

1,045 阅读3分钟

javascript 继承方法

function parent() {
  this.parentFields = 'parentFields';
  this.parentObject={key :'parentObject'}
}
parent.prototype.parentMethod = function () {
  console.log('parentMethod');
}

function child() {
  this.childFields = 'childFields';
  this.childObject={key :'childObject'}
}

child.prototype.childMethod = function () {
  console.log('childMethod');
}

一、原型链继承

父类实例作为子类原型 代码:


child.prototype=new parent();

var children=new child();

console.log(children);
console.log(children instanceof parent);
console.log(children instanceof child);

输出如下:



缺点:

  1. 不支持继承多个父类。
  2. 由以上代码可见,子类(child)的原型(prototype)被父类的实例覆盖(childMethod方法被覆盖),因此,子类原型链的方法、属性必须在child.prototype=new parent()代码后。
  3. 父类原型对象下的引用属性会被子类的实例对象所共享,即所有子类的实例共用父类原型对象下的引用属性,一个子类实例修改该引用属性,所有的子类实例都会受到影响。
child.prototype=new parent();

var children1=new child();
children1.parentObject.key='children1_ParentObject';//children1实例修改parent下的引用属性,children2实例会受到影响
var children2=new child();

console.log(children1);
console.log(children2);

二、拷贝父类属性继承

为了避免原型链继承所造成的问题,父类实例不直接覆盖子类原型,而是通过遍历父类实例的属性,逐个赋值给子类原型。

function child() {
  var p = new parent();
  for(var key in p){
    if(!child.prototype[key])
    {
        child.prototype[key] = pa[key];
    }
  }
  this.childFields = 'childFields';
  this.childObject={key :'childObject'}
}

child.prototype.childMethod = function () {
  console.log('childMethod');
}

var children1=new child();

console.log(children1);
console.log(children1 instanceof parent);//false
console.log(children1 instanceof child);

children1.parentObject.key='children1_ParentObject';//children1实例修改parent下的引用属性,children2实例会受到影响
var children2=new child();

console.log(children2);

拷贝父类属性继承解决了原型继承的一些问题,比如说可以继承多个父类,子类的原型方法不会被覆盖,但是父类原型对象下的引用属性会被子类的实例对象所共享。
缺点:

  1. 无法获取父类不可枚举的方法
  2. 子类实例不是继承父类
  3. 跟原型继承 缺点3 一样
那么解决 父类原型对象下的引用属性会被子类的实例对象所共享 的问题,用什么方法呢?答案是call方法。

三、构造继承

即在子类构造函数里通过call方法调用父类的属性。代码:

function child() {
  parent.call(this);//调用父类属性
  this.childFields = 'childFields';
  this.childObject={key :'childObject'}
}

child.prototype.childMethod = function () {
  console.log('childMethod');
}

var children=new child();

console.log(children);
console.log(children instanceof parent);//false
console.log(children instanceof child);

缺点:

  1. 没有继承父类原型。
  2. 不是父类的子类。

四、组合继承

为了解决 拷贝继承 和 构造函数 的缺点,现在把这两个继承方法组合在一起,就成了组合继承。

function child() {
  parent.call(this);//调用父类属性
  this.childFields = 'childFields';
  this.childObject={key :'childObject'}
}

child.prototype = new parent();

var children = new child();
console.log(children);
console.log(children instanceof parent);
console.log(children instanceof child);

缺点:

  1. 子类(child)的原型(prototype)被父类的实例覆盖(childMethod方法被覆盖),因此,子类原型链的方法、属性必须在child.prototype=new parent()代码后。(原型继承缺点2)
  2. 由上图可知, 由于调用两次父类构造函数,子类实例存在两份父类私有属性。

五、寄生组合继承

通过call继承父类私有属性,通过子类原型上的隐藏属性继承父类的原型。

简单版:

function child() {
  parent.call(this);//调用父类属性
  this.childFields = 'childFields';
  this.childObject={key :'childObject'}
}

child.prototype.__proto__= parent.prototype;

var children=new child();
console.log(children);
console.log(children instanceof parent);
console.log(children instanceof child);

网上版:

function child() {
  parent.call(this);//调用父类属性
  this.childFields = 'childFields';
  this.childObject={key :'childObject'}
}

function simpleNew(parentClass){
 var object = function(){};
 
 if(typeof parentClass !=='function') return new object();
 object.prototype = parentClass.prototype;
 return new object();
 
}


child.prototype = simpleNew(parent);

var children=new child();
console.log(children);
console.log(children instanceof parent);
console.log(children instanceof child);

上面两种方法,最完美的还是简单版,因为网上版还是覆盖了子类的原型。

总结:

js的继承可以分为两部分:
一是子类继承父类的私有属性;
通过call方法继承父类私有属性可以避免子类实例共用父类引用属性的问题。

二是子类继承父类的原型方法和属性。
通过子类原型的隐藏属性(prototype.proto)继承父类的原型可以避免覆盖子类原型的问题。