浅谈javscript中的继承方式(原型链继承、组合继承、原型式继承、寄生式继承、寄生组合式)

JS的继承

JS在意义上来说并没有真正的继承,它所支持的是实现继承,而且他的实现继承的主要方式是依靠原型链来完成的。

原型链继承

我们都知道每个对象都有一个[Prototype],这个所指向的是这个对象的构造函数的prototype,原型链继承就是将sub的原型对象 (sub.prototype = new super()) 等于super的实例,这样我们就可以通过原型链去找到super的属性和方法了~

function SuperType(){
    this.name = "小白";
    this.types = ["金毛","哈士奇"];
}
SuperType.prototype.sayName = function(){
    return this.name;
};
function SubType(){
    this.name = "大白"
}
//继承了SuperTyoe
SubType.prototype = new SuperType();

SubType.prototype.sayHost = function(){
    return "白哥"
};
var dog = new SuperType();

var dog1 = new SubType();
var dog2 = new SubType();
dog2.name = "老白";
dog1.types.push('柯基');

console.log(dog1.sayName(),dog2.sayName());

console.log(dog1.types,dog2.types);

// 大白 老白
//  ["金毛", "哈士奇", "柯基"] , ["金毛", "哈士奇", "柯基"]

原型链继承的缺陷

1、所有的SubType.prototype都是一个SuperType的实例,这就造成了,SuperType内的引用类型会被所有的SubType实例共享,所以更改dog1.types会影响到dog2.types

2、还有一点就是不能在不影响所有sub对象实例的情况下向超类super构造函数传递参数

借用构造函数方式

这是为了解决原型链继承方式的缺陷1说想出的办法,但是还是有缺陷的

function SuperType(name){
    this.name = name;
    this.types = ["金毛","哈士奇"];
}
SuperType.prototype.sayName = function(){
    return this.name;
};
function SubType(name){
    SuperType.call(this,name);  
}

var dog1 = new SubType("白白");
var dog2 = new SubType("嘿嘿");
dog1.types.push("柯基");
console.log(dog1.types,dog2.types);
//这样是不行的,因为我们只是使用call方法,但是sub原型链并没有改变所以找不到super的sayName
console.log(dog2.sayName(),dog1.sayName())
// ["金毛", "哈士奇", "柯基"],["金毛", "哈士奇"]
// Uncaught TypeError: dog2.sayName is not a function

借用构造函数方式缺陷

无法实现超类prototype的复用

组合继承出现了

原型链方式能够弥补借用构造函数方式的缺点,借用构造函数方式也能弥补原型链方式的缺点,那么我们组合一下不就好了?

function SuperType(name){
    this.name = name;
    this.types = ["金毛","哈士奇"];
}
SuperType.prototype.sayName = function(){
    return this.name;
};
function SubType(name){
    SuperType.call(this,name);  
}
//继承方法,通过原型链
SubType.prototype = new SuperType();
//改变构造函数
SubType.prototype.constructor = SubType;
//不做运行测试了 应该都明白了集前两种优点于一身

组合继承的缺陷

无论什么情况,都会调用两次超类的构造函数

原型式继承

原型式继承要求必须有一个对象作为另一个对象的基础

var person = {
    name:"bai",
    firends:["zhou","li"]
}
var per1 = Object.create(person);

var per2 = Object.create(person);

per1.friends.push("wang");

console.log(per2.friends);

//["zhou","li","wang"]

等等

这怎么和原型链继承一样?
Object.create到底做了什么?

Object.create是es5新增的方法
其实现是

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

对比一下原型链继承

function SubType(){
    this.name = "大白"
}
//继承了SuperTyoe
SubType.prototype = new SuperType();

这tm不就是一样的吗~~,就简化了一步自定义的构造,依然是有原型链的缺陷。

寄生式继承

function createAnother(o){
    var clone = object(o);
    //增强对象
    clone.say = function(){
        alert("hi");
    }
    return clone;
}

寄生组合式继承

function SuperType(name){
    this.name = name;
    this.types = ["金毛","哈士奇"];
}
SuperType.prototype.sayName = function(){
    return this.name;
};
function SubType(name){
    //调用超类构造函数
    this.name ="大白"
    SuperType.call(this,name);
}
function inherit(subtype,supertye){
    //创建超类原型副本
    var proto = Object.create(supertye.prototype);
    //添加构造函数为子类
    proto.constructor = subtype;
    //子类原型指向超类的副本
    subtype.prototype = proto;
}
//实现继承
inherit(SubType,SuperType);

SubType.prototype.sayHost = function(){
    return "白哥"
};

var dog1 = new SubType("小白");
var dog2 = new SubType("老白");
dog1.types.push('柯基');

console.log(dog1.sayName(),dog2.sayName());

console.log(dog1.types,dog2.types);

寄生组合式很理想了

只有一次超类构造函数的调用
还能够正常使用instanceof 和 isPrototypeOf()

并且避免了prototype上的多余的不必要的属性(因为通过 SuperType.call(this,name);已经继承过来了)

你可能感兴趣的:(js)