JS继承

原型链

利用原型让一个引用类型继承另一个引用类型的属性和方法。每个构造函数都有一个原型对象,原型对象包含一个指向构造函数的指针,实例包含一个指向原型对象的内部指针。
原型对象等于另一个类型的实例,原型对象就会包含一个指向另一个原型的指针,另一个原型包含着指向另一个构造函数的指针。而另一个原型又是另一个类型的实例如此层层递进,就构成了实例与原型的链条。

function SuperType(){
   this.property = true;
}
SuperType.prototype.getSuperValue = function(){
   return this.property;
}
function SubType(){
   this.property = false;
}
//继承SuperType
SubType.prototype = new SuperType();

SubType.prototype.getSubValue = function (){
   return this.property;
}
var instance = new SubType();
alert(instance.getSuperValue());//true

SubType通过创建SuperType的实例并将实例赋给SubType.prototype实现继承了SuperType。原来存在于SuperType的实例中的所有属性和方法现在也存在于SubType.prototype中。之后也可以向SubType.prototype添加新方法

JS继承_第1张图片
构造函数与原型的关系

我们用SuperType的实例作为SubType的原型,不仅拥有了SuperType的所有属性和方法而且还有一个指向SuperType的原型的指针。instance指向SubType的原型,SubType的原型指向SuperType的原型。getSuper Value()方法仍然还在SuperType.prototype中,property位于SubType.prototype。property是一个实例属性,getSuperValue()是一个原型方法。SubType.prototype现在式SuperType的实例,property也位于实例中。instance.constructor指向SuperType,因为SubType.prototype中的constructor被从重写了(SubType的原型指向了SuperType的原型,而这个原型对象的constructor属性指向的是SuperType)。

搜索getSuperValue()步骤:

  • 搜索实例
  • 搜索SubType.prototype
  • 搜索SuperType.prototype
1.默认的原型

所有的引用类型默认继承了Object,也是通过原型链实现的,所有函数默认原型都是Object的实例,默认原型包含指向Object.prototype,这是所有自定义类型包含toString(),valueOf()的原因。


JS继承_第2张图片
原型和实例的关系

instance是Object,SuperType,SubType任一类型的实例;

alert(instance instanceof Object);         //true
alert(instance instanceof SuperType);  //true
alert(instance instanceof SubType);     //true

在原型链中出现过的原型都是该原型链派生的实例的原型

alert(Object.prototype.isPrototypeOf(instanceof ));         //true
alert(SuperType.prototype.isPrototypeOf(instanceof ));  //true
alert(SubType.prototype.isPrototypeOf(instanceof ));     //true
3.谨慎的定义方法

给原型添加方法的代码一定要放在替换原型的语句之后

function SuperType(){
   this.property = true;
}
SuperType.prototype.getSuperValue = function(){
   return this.property;
}
function SubType(){
   this.property = false;
}
//继承SuperType
SubType.prototype = new SuperType();
//添加新方法
SubType.prototype.getSubValue = function(){
   return this.subproperty;
} 
//重写超类型中的方法
SubType.prototype.getSuperValue = function(){
   return this.subproperty;
} 
var instance = new SubType();
alert(instance.getSuperValue());//false

方法被添加到SubType会屏蔽SuperType的getSuperValue方法在SubType中调用新定义的在SuperType中调用之前的必须在实例替换原型之后


不能使用对象字面量穿件原型方法,这样会重写原型链

function SuperType(){
   this.property = true;
}
SuperType.prototype.getSuperValue = function(){
   return this.property;
}
function SubType(){
   this.property = false;
}
//继承SuperType
SubType.prototype = new SuperType();
//导致上一行代码无效
SubTyope.prototype = {
   getSubValue : function(){
     return this.subproperty;
   }
}
var instance = new SubType();
alert(instance.getSuperValue());//error!

把SupperType的实例赋值给原型,又将原型替换成一个对象字面量,SubType和SuperType之间已经没有关系啦

4.原型链的问题

应用类型值的原型属性会被所有实例共享,通过原型链原先的实例属性变成了原型属性了。

function SuperType(){
   this.colors = ["red","blue"]; 
}
function SubType(){
}
//继承SuperType
SubType.prototype = new SuperType();
var instance1 =new SubType();
instance1.colors.push("black");
alert(instance1.colors); //"red,blue,black"
var instance2 =new SubType();
alert(instance2.colors); //"red,blue,black"

SubType通过原型链继承了SuperType之后,SubType.prototype变成了SuperType的实例,就拥有了colors属性相当于创建SubType.prototype。colors属性。但SubType实例都会共享这个属性。


另一个问题:创建子类型实例时,不能在不影响所有对象实例的情况下向超类型的构造函数中传递参数

借用构造函数

在子类型的构造函数内部调用超类型的构造函数。通过使用apply()和call()在新创建的对象上执行构造函数

function SuperType(){
   this.colors = ["red","blue"]; 
}
function SubType(){
   //
   SuperType.call(this);
}
var instance1 =new SubType();
instance1.colors.push("black");
alert(instance1.colors); //"red,blue,black"
var instance2 =new SubType();
alert(instance2.colors); //"red,blue"

实际上在SubType对象上执行SuperType()函数中定义的所有对象初始化代码这样SubType的每个实例都有一个自己的colors属性的副本了。

1.传递参数
function SuperType(name){
   this.name = name;
}
function SubType(){
   SuperType.call(this,"ice");
   this.age = 20;
}
var instace = new SubType();
alert(instance.name);   //'ice'
alert(instance.age);      //20

为确保构造函数不重写子类型的属性,可以在调用超类型构造函数之后添加子类型自定义属性

2.构造函数的问题

方法在构造函数中定义,没有了函数复用,而且子类型不可见超类型原型中定义的方法。

组合继承

将原型链和借用构造函数的技术组合到一块,发挥二者之长的继承模式。既通过在原型上定义方法实现函数复用,又能保证每个实例都有自己的属性;

  • 用原型链实现对原型属性和方法的继承
  • 借用构造函数实现对实例属性的继承
function SuperType(name){
   this.name = name;
   this.colors = ["red","blue"];
}
SupperType.prototype.sayName = function(){
   alert(this.name);
}
function SubType(name,age){
   //继承属性
   SuperType.call(this,name);
   this.age = age;
}
//继承方法
SubType.prototype = new SuprtType();
SubType.prototype.sayAge = function(){
   alert(this.age);
}
var instance1 new SubType("Nicholas",29);
instance1.colors.push("black");
alert(instance1.colors); //"red,blue,black"
instance1.sayName(); //"Nicholas"
instance1.sayAge();    //29

var instance2 new SubType("Greg",27);
instance2.colors.push("black");
alert(instance2.colors); //"red,blue"
instance2.sayName(); //"Greg"
instance2.sayAge();    //27

SuperType构造函数定义了俩个属性name和colors,SuperTye的原型定义了一个方法sayName()。SubType构造函数在调用SuperType的构造函数时传入了name参数,之后定义了自己的属性age。然后将SuperType的实例赋值给SubType的原型,然后在新原型上定义了方法sayAge()。这样就可以拥有自己的属性,又使用相同的方法。
instanceof和i是Prototype Of()也能够用于识别基于组合继承创建的对象。

原型式继承

借助原型可以基于已有的对象创建新对象,同时还不必因此创建自定义类型

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

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

var person = {
   name:"ice",
   friends:["She","Cou"]
}
var person1 = object(person);
person1.name="ice";
person1.friends.push("Rob");

var person2 = object(person);
person2.name="fire";
person2.friends.push("Bar");

alert(person.friends);//:"She,Cou,Rob,Bar"

person对象作为另一个对象的基础,把他传到object()函数中,然后返回一个新对象。这个新对象将person作为原型,所以他的原型中包含一个基本类型值属性和一个引用类型值属性。这样person。friends不仅属于person所有,也被person1和person2共享,相当于创建了person对象的俩个副本。

寄生式继承

创建一个仅用于封装继承过程的函数,该函数在内部以某种方式来增强对象,像真正的它做了所有工作一样返回对象。

function createAnother(original){
   var clone = object(original);
   clone.sayHi = function(){
      alert("hi");
   }
   return clone;
}

基于original返回了一个新对象clone,clone不仅有original的所有属性和方法还有自己的sayHi()方法。

寄生组合式继承

function SuperType(name){
   this.name = name;
   this.colors = ["red","blue","green"];
}
SupperType.prototype.sayName = function(){
   alert(this.name);
}
function SubType(name,age){
   //继承属性
   SuperType.call(this,name);  //第二次调用SuperType()
   this.age = age;
}
//继承方法
SubType.prototype = new SuprtType(); //第一次调用SuperType()
SubType。prototype。constructor = SubType;
SubType.prototype.sayAge = function(){
   alert(this.age);
}

第一次调用SuperType构造函数时,SubType.prototype会得到name和colors俩个属性。它们是SuperType的实例属性,只不过位于SubType原型中。调用SubType构造函数时,又会调用一次SuperType构造函数,这一次又在新对象上创建了实例属性name和colors这俩个属性就屏蔽了原型中的同名属性。


寄生组合式继承:不必为指定子类型的原型调用超类型的构造函数,使用寄生式继承来继承超类型的原型,然后将结果指定给子类型的原型

function inheritPrototype(subType,superType){
   var prototype = object(superType.prototype);//创建对象
   prototype.constructor = subType;                  //增强对象
   subType.prototype = prototype;                     //指定对象
}

inheritPrototype()函数实现了寄生式组合继承的最简单形式。这个函数接受俩个参数:子类型构造函数和超类型构造函数。第一步创建创建超类型原型的副本。第二步为副本添加constructor属性,从而弥补重写原型而失去默认constructor属性第三步,将副本赋值给子类型的原型。

function SuperType(name){
   this.name = name;
   this.colors = ["red","blue","green"];
}
SupperType.prototype.sayName = function(){
   alert(this.name);
}
function SubType(name,age){
   SuperType.call(this,name); 
   this.age = age;
}
inheritPrototype(SubType,SuperType);
SubType.prototype.sayAge = function(){
   alert(this.age);
}
JS继承_第3张图片
image.png

此方法的高效率体现在调用了一次SuperType构造函数,并且避免了SubType.prototype上面创建不必要的多余属性,而且可以使用instanceof和isPrototypeOf()。
开发者普遍认为寄生式组合继承是继承引用类型最理想的继承方式。

你可能感兴趣的:(JS继承)