面向对象的程序设计(二)
来源:互联网 发布:好的网络兼职 编辑:程序博客网 时间:2024/06/08 06:01
三、继承
许多面向对象的语言都支持两种继承方式:
- 接口继承:只继承方法签名。
- 实现继承:继承实际的方法。
由于函数没有签名,在ECMAScript中无法实现接口继承。ECMAScript只支持实现继承,而且其实现继承主要是依靠原型链来实现。
1、原型链
1)利用原型链实现继承的基本思想
利用原型让一个引用类型继承另一个引用类型的属性和方法。
function SuperType(){ this.property = true;}SuperType.prototype.getSuperValue = function(){ return this.property;};function SubType(){ this.subproperty = false;}//继承了SuperTypeSubType.prototype = new SuperType();SubType.prototype.getSubValue = function (){ return this.subproperty;};var instance = new SubType();alert(instance.getSuperValue()); // true
上面的代码定义了两个类型:SuperType和SubType、每个类型分别有一个属性和一个方法。其中,SubType继承了SuperType,而继承是通过创建SuperType的实例,并将该实例赋给SubType.prototype实现的。实现的本质是重写原型对象,代之以一个新类型的实例。在这个例子中,instance指向SubType的原型,SubType的原型又指向SuperType的原型。
通过原型链实现继承的情况下,搜索过程沿着原型链向上搜索。以上面的例子为例,调用instance.getSuperValue()会经历三个搜索过程:搜索实例→搜索SubType.prototype→搜索SuperType.prototype,在最后一步才找到该方法。在找不到属性或方法时,搜索过程总是要一环一环地前行到原型链末端才会停下来。
2)默认的原型
所有引用类型默认都继承Object,这个继承是通过原型链实现的。
所有函数的默认原型都是Object的实例,默认原型都会包含一个内部指针,指向Object.prototype。
3)构造函数、原型和实例的关系
每个构造函数都有一个原型对象,原型对象包含一个指向指针构造函数的指针,而实例都包含一个指向原型对象的内部指针。
确定原型和实例之间关系的方式:
- 使用instanceof操作法:用这个操作符来测试实例与原型链中出现过的构造函数,结果返回true。
- 使用isPrototypeOf()方法:只要是原型链中出现过的原型,都可以说是该原型链所派生的实例原型,会返回true。
4)注意事项
- 给原型添加方法的代码一定要放在替换原型的语句之后。
function SuperType(){ this.property = true;}SuperType.prototype.getSuperValue = function(){ return this.property;};function SubType(){ this.subproperty = false;}//继承了SuperTypeSubType.prototype = new SuperType();//添加新方法SubType.prototype.getSubValue = function (){ return this.subproperty;};//重写超类型中的方法SubType.prototype.getSuperValue = function (){ return false;};var instance = new SubType();alert(instance.getSuperValue()); // false
- 在通过原型链实现继承时,不能使用对象字面量创建原型方法。因为这样做会重写原型链。
function SuperType(){ this.property = true;}SuperType.prototype.getSuperValue = function(){ return this.property;};function SubType(){ this.subproperty = false;}//继承了SuperTypeSubType.prototype = new SuperType();//使用字面量添加新方法,会导致上一行代码无效SubType.prototype = { getSubValue : function (){ return this.subproperty; }, someOtherMethod : function (){ return false; }};var instance = new SubType();alert(instance.getSuperValue()); // error
5)原型链的问题
- 在通过原型来实现继承时,原型实际上就会变成另一个类型的实例。原先的实例属性就变成了现在的原型属性。比如下面这个例子中,SubType的所有实例都会共享这一个colors属性。
function SuperType(){ this.colors = ["red", "blue", "green"];}function SubType(){}SubType.prototype = new SuperType();var c1 = new SubType();c1.colors.push("black");alert(c1.colors); // red,blue,green,blackvar c2 = new SubType();alert(c2.colors); // red,blue,green,black
- 在创建子类型的实例时,不能向超类型的构造函数中传递参数。
2、借用构造函数(伪造对象或经典继承)
1)基本思想:在子类型构造函数的内部调用超类型构造函数。
function SuperType(){ this.colors = ["red", "blue", "green"];}function SubType(){ //继承了SuperType SuperType.call(this);}var c1 = new SubType();c1.colors.push("black");alert(c1.colors); // red,blue,green,blackvar c2 = new SubType();alert(c2.colors); // red,blue,green
2)优势(相对于原型链):可以在子类型构造函数中向超类型构造函数传递参数。
function SuperType(name){ this.name = name;}function SubType(){ //继承了SuperType,同时还传递了参数 SuperType.call(this,"T"); //实例属性 this.age = 12;}var p = new SubType();alert(p.name); //Talert(p.age); // 12
3)问题:
- 方法都在构造函数中定义,函数的复用无从谈起。
- 在超类型的原型中定义的方法,对于子类型而言不可见,结果所有类型都只能使用构造函数模式。
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("T", 29);instance1.colors.push("black");alert(instance1.colors); //"red,blue,green,black"instance1.sayName(); //"T";instance1.sayAge(); //29var instance2 = new SubType("Greg", 27);alert(instance2.colors); //"red,blue,green"instance2.sayName(); //"Greg";instance2.sayAge(); //27
4、原型式继承
借助原型可以基于已有的对象创建新对象,不必创建自定义类型。
看下面的代码,在object()函数内部,先创建了一个临时性的构造函数,然后将传入的对象作为这个构造函数的原型,最后返回了这个临时类型的一个新实例。
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("Barbie");alert(person.friends); //"Shelby,Court,Van,Rob,Barbie"
ECMAScript 5 通过新增Object.create()方法规范化了原型式继承。
该方法接收两个参数:一个是用作新对象原型的对象和(可选)一个新对象定义额外属性的对象。
//传入第一个参数var person = { name: "Nicholas", friends: ["Shelby", "Court", "Van"]};var anotherPerson = Object.create(person);anotherPerson.name = "Greg";anotherPerson.friends.push("Rob");var yetAnotherPerson = Object.create(person);yetAnotherPerson.name = "Linda";yetAnotherPerson.friends.push("Barbie");alert(person.friends); //"Shelby,Court,Van,Rob,Barbie"//传入第二个参数var person = { name: "Nicholas", friends: ["Shelby", "Court", "Van"]};var anotherPerson = Object.create(person, { name: { value: "Greg" }});alert(anotherPerson.name); //"Greg"
5、寄生式继承
与寄生构造函数和工厂模式类似,创建一个仅用于封装继承过程的函数,该函数在内部以某种方式来增强对象,最后返回对象。
function createAnother(original){ var clone = Object(original); //通过调用函数创建一个新对象 clone.sayHi = function(){ //以某种方式来增强这个对象 alert("hi"); }; return clone; //返回这个对象}var p1 = { name: "T", friends: ["A", "B", "C"]};var p2 = createAnother(p1);p2.sayHi(); // hi
6、寄生组合式继承
1)组合继承的问题
无论什么情况下,都会调用两次超类型构造函数,一次是在创建子类型原型的时候,另一次是在子类型构造函数内部。
2)寄生组合式继承。是指通过借用构造函数来继承属性,通过原型链的混成形式来继承方法。
基本思路是:不必为了指定子类型的原型而调用超类型的构造函数,我们需要的只是超类型原型的一个副本而已。
//寄生组合式继承的基本模式function inheritPrototype(subType,superType){ var prototype = Object(superType.prototype); //创建对象 prototype.constructor = subType; //增强对象 subType.prototype = prototype; //指定对象}
- Qt面向对象的程序设计(二)
- 面向对象的程序设计(二)
- 面向对象程序设计(二)
- JavaScript学习——面向对象的程序设计(二)
- LUA面向对象程序设计(二)继承
- 面向对象总结(二)程序设计原则
- 第六章 面向对象的程序设计(二) JavaScript高级程序设计
- java面向对象程序设计(二)
- 面向对象的程序设计(理解对象)
- 面向对象的程序设计(创建对象)
- 面向对象的程序设计
- 面向对象的程序设计
- 面向对象的程序设计
- 面向对象的程序设计
- 面向对象的程序设计
- 面向对象的程序设计
- 面向对象的程序设计
- 面向对象的程序设计
- java 计算两个日期之间的天数
- 微信小程序例子——点击文字实现页面跳转
- 572. Subtree of Another Tree
- caffe(2)配置文件.prototxt的理解
- 测试spring和mybatis出现TransactionAwareDataSourceProxy
- 面向对象的程序设计(二)
- Maven仓库理解、如何引入本地包、Maven多种方式打可执行jar包
- mysql基础语法篇
- 字符串:KMP Eentend-Kmp 自动机 trie图 trie树 后缀树 后缀数组
- 《Flask Web开发》学习笔记之bug--(7)【ImportError: cannot import name 'db'】
- android StackTraceElement的一些用法
- linux基础学习
- 陈耀硕:黄金交易中资金管理的7个基本原则
- 科学中的人工智能革命