对象继承

js中的继承主要是依靠原型链来实现的,所谓的原型链就是让一个引用类型继承另一个引用类型的属性和方法。

一、原型链

先来明确一下原型、实例以及构造函数之间的关系:

(1)每一个构造函数都有一个原型对象(prototype)

(2)每一个原型对象都包含一个指向构造函数的的指针(constructor)

(3)每一个实例都包含一个指向原型对象的内部指针([[prototype]])

接下来,如果我们让一个原型对象等于一个另一个类型的实例。那么根据上述关系,这时这个原型对象实际上就是另一个类型的实例,那么它将会包含一个指向该实例的原型对象的指针。这个被指向的原型对象的指针,又有它自己指向的构造函数,那么,假如另一个原型对象也是另一个构造函数的实例,那么就又会有上述的关系,层层递进,就形成了我们所说的原型链。

function SuperType(){
	this.property=true;
}
SuperType.prototype.getSuperValue=function(){
	return this.property;
};
function SubType(){
	this.subproperty=false;
}

//继承了SuperType
SubType.prototype=new SuperType();
SubType.prototype.getSubValue=function(){
	return this.subprototype;
};
var instance=new SubType();
console.log(instance.getSuperValue());//true

  (我们在这里称SubType类型为子类型,而SuperType为超类型)实际上,上面的代码中,我们没有使用SubType默认的原型对象,而是利用SuperType的原型将其进行重写,然后再这个基础之上进行修改。在进行继承之后,我们再次使用instanceof操作符来进行判定对象的类型就能发现这几种原型之间的关系:

console.log(instance instanceof Object);//true
console.log(instance instanceof SuperType);//true
console.log(instance instanceof SubType);//true

  所有的对象都是继承自Object类型的,所以在上面的第一行代码中将会返回true。只要是原型链中出现过的原型,都可以说是该原型链所派生的实例的原型。而且,给原型添加方法的代码一定要放在替换原型的语句之后。要记住,我们在通过原型链实现继承的时候,不要使用字面量的方式修改原型方法,因为这会重写原型链。

原型链存在的问题:因为原型链是通过把一个引用类型的实例替换掉另一个引用类型的原型。所以,原先在前者中是属于实例属性的到现在变成了另一个引用类型的原型,被所有实例共享,这是我们所不想达到的效果。看一下这个例子:

function SuperType(){
	this.colors=["red","blue","green"];
}

function Subtype(){
}
//继承了SuperType
SuperType.prototype=new SuperType();//创建实例,赋予原型,导致原型实例属性现变成了原型中的共享属性

var instance1=new SubType();
instance.colors.push("black");
console.log(instance1.colors);//"red,blue,green,black"

var instance2=new SubType();
console.log(instance2.colors);//"red,blue,green,black"

  

二、借用构造函数:在子类型构造函数的内部调用超类型的构造函数

为了解决上面的问题,我们使用一种叫做借用构造函数的技术。只要利用利用call或者apply方法,我们就可以在新创建的对象上执行构造函数了

function SuperType(){
	this.colors=["red","blue","green"];
}
function SubType(){
	//继承了SuperType
	SuperType.call(this);
}

var instance1=new SubType();
instance1.colors.push("black");
console.log(instance1.colors);//"red,blue,green,black"

var instance2=new SubType();
console.log(instance2.colors);//"red,blue,green"

 

三、组合继承

通过在原型上定义方法实现函数复用,并保证每个实例都有它自己的属性

function SuperType(name){
this.name=name;
this.colors=["red","blue","green"];
}
SuperType.prototype.sayName=function(){
console.log(this.name);
};
function SubType(name,age){
SuperType.call(this,name);
this.age=age;
}
SubType.prototype=new SuperType();
SubType.prototype.constructor=SubType;
SubType.prototype.sayAge=function(){
console.log(this.name);
};

 组合继承先通过借用构造函数,让SuperType构造函数在SubType构造函数内部以SubType构造函数的作用域执行代码,接着创建一个新的SuperType的实例并复制给SubType.prototype。这样SuperType中的实例属性就成为了原型中的属性,在使用sayName的时候,实际上调用的还是SuperType的原型方法。而且这里的colors,在两个不同的构造函数中,是不同的两个属性,因为SuperType.call(this,name)这一行代码。

 

四、原型式继承

原型式继承的核心函数是下面的代码

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

在这个函数内部,先创建一个临时的构造函数,然后将传入的对象作为这个构造函数的原型,最后返回了这个临时类型的一个新实例。从本质上讲这是对传入对象的一次浅复制。

我们可以这样使用原型式继承:

function object(o){
                function F(){}
                F.prototype=o;
                return new F();
            }
            var Person={
                name:"Nicholas",
                friends:["Shelby","Court","Van"]
            };
            var anotherPerson=object(person);
            anotherPerson.name="Greg";
            anotherPerson.friends.push("Rob");
            var yetAnotherPerson=object(person);
            yetAnotherPerson.name="Linda";
            yetAnotherPerson.friends.push("Baribie");
            console.log(person.friends);//"Shelby,Court,Van,Rob,Baribie"

可以发现两个对象都是公用同一个引用类型属性,他们实际上只是Person的两个副本,我们通常利用Object.create()方法规范化原型式继承:

var person={
                name:"Nicholas",
                friends:["Shelby","Court","Van"]
            };
            var anotherPerson=Object.create(person);
            anotherPerson.name="Greg";
            anotherPerson.frineds.push("Pob");
            var yetanotherPerson=Object.create(person);
            anotherPerson.name="Linda";
            anotherPerson.friends.push("Barible");

五、寄生式继承

寄生式继承类似原型式继承,引入一个对象,然后进行原型式继承得到一个新对象,再对这个函数进行加强

function createAnother(original){
                var clone=object(original);
                clone.sayHi=function(){
                    console.log("hi");
                };
                return clone;
            }
            var Person={
                name:"Nicholas",
                friends:["shelby","Court","Van"];
            };
            var anotherPerson=createAnother(Person);
            anotherPerson.sayHi();//"hi"

新对象不仅继承Person对象所有的属性和方法,还有自己的sayHi()方法

六、组合寄生式继承

这是js中最常用的继承模式了,所谓寄生组合式继承,即通过借用构造函数来继承属性,通过原型链的混成形式来继承方法,避免两次调用超类型的构造函数,使得在子类型的实例和圆形中都各有一套相同的属性和方法。

实现寄生组合式继承的基本模式如下所示:

function inheritPrototype(subType,superType){
                var prototype=object(superType.prototype);
                prototype.constructor=subType;
                subType.prototype=prototype;
            }

首先创建一个超类型原型的一个副本,然后为创建的副本添加constructor属性,最后将这个副本赋值给子类型的原型。

具体创建如下:

function SuperType(){
                this.name=name;
                this.colors=["red","blue","green"];
            }
            SuperType.prototype.sayName=function(){
                console.log(this.name);
            };
            function SubType(name,age){
                SuperType.call(this,name);
                this.age=age;
            }
            function inheritProtoype(SubType,SuperType){
                var prototype=object(SuperType.prototype);
                prototype.constructor=SubType;
                SubType.prototype=prototype;
            }

 

posted @ 2017-03-28 19:02  某个润滑君  阅读(146)  评论(0)    收藏  举报