Error message here!

Hide Error message here!

忘记密码?

Error message here!

请输入正确邮箱

Hide Error message here!

密码丢失?请输入您的电子邮件地址。您将收到一个重设密码链接。

Error message here!

返回登录

Close

Javascript面向对象的程序设计 —— 继承

向阳花7 2019-07-23 19:50:00 阅读数:18 评论数:0 点赞数:0 收藏数:0

许多OO语言都支持两种继承方式:
接口继承:只继承方法签名;
实现继承:继承实际的方法。
ECMAScript只支持实现继承,实现继承是继承实际的方法,依靠原型链来实现。

1、原型链

原型链是实现继承的主要方法。

基本思想:利用原型链让一个引用类型继承另一个引用类型的属性和方法。

构造函数、原型和实例的关系:每一个构造函数都有一个原型对象,原型对象都包含一个指向构造函数的指针,而实例都包含一个指向原型对象的内部指针。

实例与原型之间的链条:
假如原型对象等于另一个类型的实例,原型对象将包含一个指向另一个原型的指针,另一个原型也包含着一个指向另一个构造函数的指针。假如另一个原型又是另一个类型的实例,那么上述关系依然成立,如此层层递进,构成了实例与原型的链条。

 function SuperType(){
     this.property = true;
 }
 SuperType.prototype.getSuperValue = function(){
     return this.property;
 };
 function SubType(){
     this.subproperty = false;
 }
 
 //继承了SuperType
 SubType.prototype = new SuperType();
 
 SubType.prototype.getSubType = function(){
     return this.subproperty;
 };
 
 var instance = new SubType();
 alert(instance.getSuperValue());  //true
 // instance.constructor现在指向SuperType

在通过原型链实现继承的情况下,搜索过程就得以沿着原型链继续向上。

原型链存在的问题
 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"
SubType的所有实例都会共享一个colors属性,对instance1.colors的修改能够通过instance2.colors反映出来。

第二个问题:在创建子类型的实例时,不能向超类型的构造函数中传递参数。没有办法在不影响所有对象实例的情况下,给超类型的构造函数传递参数。所以实践中很少会单独使用原型链。


2、借用构造函数

也叫伪造对象或经典继承。

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

 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"

(1)、传递参数

相对原型链,借用构造函数有一个很大优势:可以在子类型构造函数中向超类型构造函数传递参数。
 function SuperType(name){
     this.name = name;
 }
 function SubType(){
     //继承了SuperType,同时传递了参数
     SuperType.call(this, "Anna");
     //调用超类型构造函数后添加子类型属性,防止超类型构造函数重写子类型属性
     this.age = 26;
 }
 var instance = new SubType();
 alert(instance.name); // "Anna"
 alert(instance.age); //

(2)、借用构造函数的问题

方法都在构造函数中定义,函数复用不能实现。超类型原型中定义的方法,子类型中不可见,结果所有类型都只能使用构造函数模式。很少单独使用。


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);
     this.age = age;
 }
 //继承方法
 SubType.prototype = new SuperType();
 SubType.prototype.sayAge = function(){
     alert(this.age);
 };
 
 var instance1 = new SubType("Anna", 26);
 instance1.colors.push("black");
 alert(instance1.colors);  // "red,blue,green,black"
 instance1.sayName();  // "Anna"
 instance1.sayAge();  //
 
 var instance2 = new SubType("Greg", 27);
 alert(instance2.colors);  // "red,blue,green"
 instance2.syaName();  // "Greg"
 instance2.sayAge();  //


4、原型式继承

借助原型可以基于已有的对象创建新对象,同时不必因此创建自定义类型。
 function object(o){
     function F(){}
     P.prototype = o;
     return new F();
 }
 var person = {
     name: "Anna",
     friends: ["Court", "Van", "Lily"]
 };
 
 var anotherPerson = object(person);
 anotherPerson.name = "Greg";
 anotherPerson.friends.push("Bob");
 
 var yetAnotherPerson = object(person);
 yetAnotherPerson.name = "Linda";
 yetAnotherPerson.friends.push("Barbie");
 
 alert(person.fiends); // "Court,Van,Bob,Barbie"

ECMAScript5新增Object.create()方法规范化了原型式继承,该方法接收2个参数:用作新对象原型的对象和(可选)一个为新对象定义额外属性的对象。

 var person = {
     name: "Anna",
     friends: ["Court", "Van", "Lily"]
 };
 
 var anotherPerson = Object.create(person);
 anotherPerson.name = "Greg";
 anotherPerson.friends.push("Bob");
 
 var yetAnotherPerson = Object.create(person);
 yetAnotherPerson.name = "Linda";
 yetAnotherPerson.friends.push("Barbie");
 
 alert(person.fiends); // "Court,Van,Bob,Barbie"
 
 Object.create()的第二个参数的格式每个属性都是通过自己的描述符定义的,以这种方式指定的任何属性都会覆盖原型对象上的同名属性。
 var person = {
     name: "Anna",
     friends: ["Court", "Van", "Lily"]
 };
 var anotherPerson = Object.create(person, {
     name: {
         value: "Greg"
     }
 });
 alert(anotherPerson.name); // "Greg"

支持Object.create()方法的浏览器:IE9+、 Firefox4+、 Safari5+、 Opera12+和Chrome。

在只想让一个对象保持类似的情况下,原型式继承完全可以胜任。但是,包含引用类型值的属性始终都会共享相应的值。


5、寄生式继承

思路:创建一个用于封装继承过程的函数,该函数的内部已某种方式来增强对象,最后再像真的是它做了所有工作一样返回对象。

 function object(o){
     function F(){}
     P.prototype = o;
     return new F();
 }
 function createAnother(original){
     var clone = object(original);  // 通过调用函数创建一个新对象
     clone.sayHi = function(){      // 以某种方式来增强这个对象
         alert("hi");
     };
     return clone;                  //返回这个对象
 }
 var person = {
     name: "Anna",
     friends: ["Court", "Van", "Lily"]
 };
 var anotherPerson = createAnother(person);
 anotherPerson.sayHi(); // "hi"

主要考虑对象而不是自定义类型和构造函数的情况下,寄生式继承也是一种有用的模式。上面示范继承模式时使用的object()函数不是必需的;任何能够返回新对象的函数都适应于此模式。


6、寄生组合式继承

组合继承是JS最常用的继承模式,但也有不足:无论什么情况下,都会调用两次超类型构造函数,一次是在创建子类型原型的时候,另一次是在子类型构造函数内部。子类型会包含超类型对象的全部实例属性,但在调用子类型构造函数是会重写这些属性。
 function SuperType(name){
     this.name = name;
     this.colors = ["red", "blue", "green"];
 }
 SuperType.prototype.sayName = function(){
     alert(this.name);
 };
 function SubType(name, age){
     //继承属性,第二次调用SuperType(),name和colors属性屏蔽了原型中的同名属性
     SuperType.call(this, name);
     this.age = age;
 }
 //继承方法,第一次调用SuperType(),得到属性:name和colors
 SubType.prototype = new SuperType();
 SubType.prototype.constructor = SubType;
 SubType.prototype.sayAge = function(){
     alert(this.age);
 };

寄生组合式继承解决了这个问题。

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

基本思路:不必为了指定子类型的原型而调用超类型的构造函数,只需要超类型原型的副本即可。使用寄生式继承来继承超类型的原型,再将结果指定给子类型的原型。

 function object(o){
     function F(){}
     P.prototype = o;
     return new F();
 }
 function inheritPrototype(subType, superType){
     var prototype = object(superType.prototype);  //创建对象
     prototype.constructor = subType;              //增强对象
     subType.prototype = prototype;                //指定对象
 }
 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()。

普遍认为寄生组合式继承是引用类型最理想的继承范式。

















版权声明
本文为[向阳花7]所创,转载请带上原文链接,感谢
https://www.cnblogs.com/wuxxblog/p/11234020.html