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);
输出如下:

缺点:
- 不支持继承多个父类。
- 由以上代码可见,子类(child)的原型(prototype)被父类的实例覆盖(childMethod方法被覆盖),因此,子类原型链的方法、属性必须在child.prototype=new parent()代码后。
- 父类原型对象下的引用属性会被子类的实例对象所共享,即所有子类的实例共用父类原型对象下的引用属性,一个子类实例修改该引用属性,所有的子类实例都会受到影响。
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);
拷贝父类属性继承解决了原型继承的一些问题,比如说可以继承多个父类,子类的原型方法不会被覆盖,但是父类原型对象下的引用属性会被子类的实例对象所共享。
缺点:
- 无法获取父类不可枚举的方法
- 子类实例不是继承父类
- 跟原型继承 缺点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);

- 没有继承父类原型。
- 不是父类的子类。
四、组合继承
为了解决 拷贝继承 和 构造函数 的缺点,现在把这两个继承方法组合在一起,就成了组合继承。
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);

- 子类(child)的原型(prototype)被父类的实例覆盖(childMethod方法被覆盖),因此,子类原型链的方法、属性必须在child.prototype=new parent()代码后。(原型继承缺点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)继承父类的原型可以避免覆盖子类原型的问题。