javascript-继承

1. 原型链javascript

原型链是js中实现继承的主要方法,其基本思想是利用原型让一个引用类型继承另外一个引用类型的属性和方法。java

实现原型链有一种基本模式,其代码大体以下:app

 SubType 继承了 SuperType,而继承是经过建立 SuperType 的实例,并将该实例赋给 SubType.prototype 实现的。实现的本质是重写原型对象,代之以一个新类型的实例。在下面例子中,咱们没有使用 SubType 默认提供的原型,而是给它换了一个新原型;这个新原型就是 SuperType 的实例。函数

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.subproperty; 
}; 
 
var instance = new SubType(); 
alert(instance.getSuperValue());      //true 

注意:instance.constructor 如今指向的是 SuperType,这是由于原来 SubType.prototype 中的 constructor 被重写了的缘故。实际上,不是 SubType 的原型的 constructor 属性被重写了,而是 SubType 的原型指向了另外一个对象—— SuperType 的原型,而这个原型对象的 constructor 属性指向的是 SuperType。this

因为原型链的关系,咱们能够说 instance 是 Object、SuperType 或 SubType 中任何一个类型的实例。spa

原型链继承的问题:1)包含引用类型值的原型。包含引用类型值的原型属性会被全部实例共享;(以下例)2):在建立子类型的实例时,不能向超类型的构造函数中传递参数。实践中不多会单独使用原型链prototype

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

2. 借用构造函数(伪造对象或经典继承)设计

基本思想:在子类型构造函数的内部调用超类型构造函数。对象

函数只不过是在特定环境中执行代码的对象, 所以经过使用 apply()和 call()方法也能够在(未来)新建立的对象上执行构造函数。blog

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

代码中加粗的那一行代码“借调”了超类型的构造函数。经过使用 call()方法(或 apply()方法 也能够),咱们其实是在(将来将要)新建立的 SubType 实例的环境下调用了 SuperType 构造函数。 这样一来,就会在新 SubType 对象上执行 SuperType()函数中定义的全部对象初始化代码。结果, SubType 的每一个实例就都会具备本身的 colors 属性的副本了。  

优势:能够在子类型构造函数中向超类型构造函 数传递参数。

function SuperType(name){    
  this.name = name; 
} 
 
function SubType(){       
  //继承了 SuperType,同时还传递了参数     
  SuperType.call(this, "Nicholas");          
  //实例属性     
  this.age = 29; 
} 
 
var instance = new SubType(); 
alert(instance.name);    //"Nicholas"; 
alert(instance.age);     //29 

以上代码中的 SuperType 只接受一个参数 name,该参数会直接赋给一个属性。在 SubType 构造 函数内部调用 SuperType 构造函数时,其实是为 SubType 的实例设置了 name 属性。

为了确保 SuperType 构造函数不会重写子类型的属性,能够在调用超类型构造函数后,再添加应该在子类型中 定义的属性。  

缺点:若是仅仅是借用构造函数,那么也将没法避免构造函数模式存在的问题——方法都在构造函数中定 义,所以函数复用就无从谈起了。并且,在超类型的原型中定义的方法,对子类型而言也是不可见的,结 果全部类型都只能使用构造函数模式。

3. 组合继承(伪经典继承)

将原型链和借用构造函数的技术组合到一块,从而发挥两者之长的一种继承模式。使用原型链实现对原型属性和方法的继承,而经过借用构造函数来实现对实例属性的继承。这样,既经过在原型上定义方法实现了函数复用,又可以保证每一个实例都有它本身的属性。

function SuperType(name){    
  this.name = name;     
  this.colors = ["red", "blue", "green"]; 
}  
SuperType.prototype.sayName = function(){     
  alert(this.name);
};  
function SubType(name, age){    
  //继承属性    
  SuperType.call(this, name);   //第二次调用 SuperType()      
  this.age = age;
 }  
//继承方法 
SubType.prototype = new SuperType();  //第一次调用 SuperType()
SubType.prototype.constructor = SubType; 
SubType.prototype.sayAge = function(){     
  alert(this.age);
 }; 
 
var instance1 = new SubType("Nicholas", 29); 
instance1.colors.push("black"); 
alert(instance1.colors);      //"red,blue,green,black" 
instance1.sayName();          //"Nicholas";
 instance1.sayAge();           //29 
 
var instance2 = new SubType("Greg", 27); 
alert(instance2.colors);      //"red,blue,green" 
instance2.sayName();          //"Greg"; 
instance2.sayAge();           //27   

组合继承是 JavaScript 经常使用的继承模式。

组合继承避免了原型链和借用构造函数的缺陷,融合了它们的优势,成为 JavaScript 中经常使用的继 承模式。并且,instanceof 和 isPrototypeOf()也可以用于识别基于组合继承建立的对象。

缺点:不管什么状况下,都会调用两次超类型构造函数:一次是在建立子类型原型的时候,另外一次是 在子类型构造函数内部。  

4. 原型式继承

道格拉斯·克罗克福德在 2006年写了一篇文章,题为 Prototypal Inheritance in JavaScript (JavaScript 中的原型式继承)。

这种方法并无使用严格意义上的构造函数。而是借助原型能够基于已有的对象建立新对象,同时还没必要所以建立自定义类型。

例:在 object()函数内部,先建立了一个临时性的构造函数,而后将传入的对象做为这个构造函数的原型,最后返回了这个临时类型的一个新实例。

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

从本质上讲,object()对传入其中的对象执行了一次浅复制。  

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("Barbie"); 
 
alert(person.friends);   //"Shelby,Court,Van,Rob,Barbie" 

克罗克福德主张的这种原型式继承,要求你必须有一个对象能够做为另外一个对象的基础。若是有这么 一个对象的话,能够把它传递给 object()函数,而后再根据具体需求对获得的对象加以修改便可。  

ECMAScript 5经过新增 Object.create()方法规范化了原型式继承。这个方法接收两个参数:一 个用做新对象原型的对象和(可选的)一个为新对象定义额外属性的对象。例:

var person = {     
  name: "Nicholas",     
  friends: ["Shelby", "Court", "Van"]
};  
 
var anotherPerson = Object.create(person, {      
  name: {         
     value: "Greg"     
  } 
});      
alert(anotherPerson.name); //"Greg" 

应用场景:在没有必要兴师动众地建立构造函数,而只想让一个对象与另外一个对象保持相似的状况下。

缺点:包含引用类型值的属性始终都会共享相应的值。

5. 寄生式继承

寄生式(parasitic)继承是与原型式继承紧密相关的一种思路,而且一样也是由克罗克福德推而广之的。

寄生式继承的思路与寄生构造函数和工厂模式相似,即建立一个仅用于封装继承过程的函数,该 函数在内部以某种方式来加强对象,后再像真地是它作了全部工做同样返回对象。

注意:示 范继承模式时使用的 object()函数不是必需的;任何可以返回新对象的函数都适用于此模式。

function createAnother(original){     
  var clone = object(original);  //经过调用函数建立一个新对象     
  clone.sayHi = function(){      //以某种方式来加强这个对象        
     alert("hi");    
  };     
return clone;         //返回这个对象 
} 
 

在这个例子中,createAnother()函数接收了一个参数,也就是将要做为新对象基础的对象。然 后,把这个对象(original)传递给 object()函数,将返回的结果赋值给 clone。再为 clone 对象 添加一个新方法 sayHi(),后返回 clone 对象。

 createAnother()函数使用:  

var person = {    
  name: "Nicholas",    
  friends: ["Shelby", "Court", "Van"] 
};  
var anotherPerson = createAnother(person);  
anotherPerson.sayHi(); //"hi" 

这个例子中的代码基于 person 返回了一个新对象——anotherPerson。新对象不只具备 person 的全部属性和方法,并且还有本身的 sayHi()方法。  

应用场景:主要考虑对象而不是自定义类型和构造函数的状况。

缺点:使用寄生式继承来为对象添加函数,会因为不能作到函数复用而下降效率;这一 点与构造函数模式相似。

6. 寄生组合式继承  

寄生组合式继承,即经过借用构造函数来继承属性,经过原型链的混成形式来继承方法。

基本思路是:没必要为了指定子类型的原型而调用超类型的构造函数,咱们所须要的无非就是超类型 原型的一个副本而已。本质上,就是使用寄生式继承来继承超类型的原型,而后再将结果指定给子类型的原型。

寄生组合式继承的基本模式以下所示。

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"]; 
} 
 
SuperType.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); 
};

这个例子的高效率体如今它只调用了一次 SuperType 构造函数,而且所以避免了在 SubType. prototype 上面建立没必要要的、多余的属性。与此同时,原型链还能保持不变;所以,还可以正常使用 instanceof 和 isPrototypeOf()。开发人员广泛认为寄生组合式继承是引用类型理想的继承范式。
 

 

javascript高级程序设计-继承-总结

相关文章
相关标签/搜索