JS Pro-深入面向对象的程序设计之继承的详解


Posted in Javascript onMay 07, 2013

原型链(prototype chaining):

利用原型来继承属性和方法。回顾一下构造函数(constructor),原型对象(prototype)和实例(instance)的关系。每一个构造函数都有一个prototype属性,该属性指向一个prototype对象;prototype对象也有constructor属性,指向该函数;而实例也有一个内部指针(__proto__)指向这个prototype对象。如果这个prototype对象是另外一个对象的实例会是怎样的呢?这样该prototype对象就包含一个指向另一个类型的指针,相应地,另一个原型中也包含着一个指向另一个构造函数的指针。

JS的继承很简单,就是把子类的prototype设为父类的一个(实例化)对象

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

最终的结果:instance的__proto__指向SubType.prototype对象,而SubType.prototype对象的__proto__属性又指向SuperType.prototype对象。getSuperValue()是一个方法,所以仍然存在于原型中,而property是一个实例属性,所以现在存在于SubType.prototype这个实例中。  instance.constructor现在指向的是SuperType,这是由于SubType.prototype指向SuperType.prototype,而SuperType.prototype的constructor属性指向SuperType函数,所以instance.constructor指向SuperType。

默认情况下,所有引用类型都继承Object。这是因为所有函数的原型对象,默认都是Object的一个实例,所以内部prototype(__proto__)指向Object.Prototype。

原型和实例的关系:可以使用2种方法来确定原型与实例之间的关系。
- instancef操作符:使用该操作符来测试实例与原型链中出现过的构造函数,都会返回true

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

- isPrototypeOf()方法:只要是原型链中出现过的原型,都可以说是该原型链所派生的实例的原型。
alert(Object.prototype.isPrototypeOf(instance)); //true
alert(SuperType.prototype.isPrototypeOf(instance)); //true
alert(SubType.prototype.isPrototypeOf(instance)); //true

给子类添加方法的注意点:我们有的时候会给子类添加方法,或者是重写父类的某些方法。这个时候就要注意,这些方法必须在继承后再定义。以下的例子里,SubType在继承SuperType后,我们给它添加了新的方法getSubValue(),而且重写了getSuperValue()方法。对于后者,只有SubType的实例才会使用重写的方法,SuperType的实例还是会使用原有的getSuperValue()方法。
function SuperType(){
this.property = true;
}
SuperType.prototype.getSuperValue = function(){

return this.property;
};
function SubType(){

t his.subproperty = false;
}
//inherit from SuperType
SubType.prototype = new SuperType();
//new method
SubType.prototype.getSubValue = function (){

return this.subproperty;
};
//override existing method
SubType.prototype.getSuperValue = function (){

return false;
};
var instance = new SubType();
alert(instance.getSuperValue()); //false

另外一个需要注意的是,通过原型链实现继承时,不能使用对象字面量创建原型方法,因为这样会重写原型链。如下面的代码,在SubType继承SuperType以后,使用对象字面量给原型添加方法,但这样做,会重写SubType原型,重写后的SubType.prototype包含的是一个Object的实例,从而也切断了与SuperType的关系。
function SuperType(){
this.property = true;
}
SuperType.prototype.getSuperValue = function(){

return this.property;
};
function SubType(){

this.subproperty = false;
}
//inherit from SuperType
SubType.prototype = new SuperType();
//try to add new methods - this nullifies the previous line
SubType.prototype = {

getSubValue : function (){


return this.subproperty;

},

someOtherMethod : function (){


return false;

}
};
var instance = new SubType();
alert(instance.getSuperValue()); //error!

原型链的问题:和原型一样,当使用引用类型值的时候,原型链就会出问题了。回顾一下之前的内容,包含一个引用类型值的原型属性会被所有实例共享,这就是为什么我们要把引用类型值在构造函数中定义,而不是在原型中定义。在通过原型链实现继承时,原型实际上会变成另一个类型的实例,于是,原先的实例属性也顺利成章的变成现在的原型属性了。
function SuperType(){
this.colors = [“red”, “blue”, “green”];
}
function SubType(){
}
//inherit from 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”

在SuperType构造函数中,我们定义了一个colors数组,每一个SuperType实例都会拥有它自己的这个colors数组。但是当SubType使用原型链继承SuperType以后,SubType.prototype变成SuperType的一个实例,因此它拥有自己的colors属性,也就是说SubType.prototype.colors属性。所以,当创建SubType实例的时候,所有实例都共享这一属性了。如上面的代码所示。

第二个问题就是:在创建子类的实例时,不能向超类的构造函数中传递参数。实际上,应该说是没有办法在不影响所有对象实例的情况下,给超类的构造函数传递参数。由于这些问题,我们不会单独使用原型链。

--------------------------------------------------------------------------------

借用构造函数(Contructor stealing):
为了解决上述问题,开发人员发明了一种叫做借用构造函数的技术。这种技术的思路就是:在子类型构造函数的内部调用超类型构造函数。(函数,只不过是在特定环境中执行代码的对象?)我们可以通过使用apply()或call()方法在新创建的对象上执行构造函数。

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

//inherit from 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”

我们在SubType里使用call()方法调用SuperType的构造函数,实际上就是在新的SubType对象上执行SuperType()函数中定义的所有对象初始化代码。结果就是每个SubType实例都具有自己的colors属性的副本。

传递参数:使用借用构造函数方法的一个很大的好处在于就是,我们可以从子类的构造函数传递参数到父类的构造函数中。

function SuperType(name){
this.name = name;
}
function SubType(){

//inherit from SuperType passing in an argument

SuperType.call(this, “Nicholas”);

//instance property

this.age = 29;
}
var instance = new SubType();
alert(instance.name); //”Nicholas”;
alert(instance.age); //29

新的SuperType构造函数新增了一个参数name,我们在call SuperType的同时,往SuperType传递参数"Nicholas"。为了不让超类型的构造函数重写子类型的属性,可以在调用超类型构造函数后再定义子类的属性。

借用构造函数的问题:方法都在构造函数中定义,无法复用。而且在超类型的原型中定义的方法,对子类型而言是不可见的。结果所有类型都只能使用构造函数模式。

--------------------------------------------------------------------------------

组合继承:
结合原型链及借用构造函数各自的优点的一种继承模式。使用原型链继承原型属性及方法,使用借用构造函数来继承实例属性。如下面例子,我们使用call()方法调用SuperType的构造函数(每个SubType实例都拥有自己的name和colors属性,以及SubType的age属性);然后再把SuperType实例赋值给SubType的原型,使其继承SuperType的sayName()方法(每个实例都共用这个方法)。

function SuperType(name){
    this.name = name;
    this.colors = ["red", "blue", "green"];
}
SuperType.prototype.sayName = function(){
    alert(this.name);
};
function SubType(name, age){
    //inherit properties
    SuperType.call(this, name);
    this.age = age;
}
//inherit methods
SubType.prototype = new SuperType();
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

原型式继承(Prototypal Inheritance):
function object(o){
function F(){}

F.prototype = o;

return new F();
}
--------------------------------------------------------------------------------

寄生式继承(Parasitic Inheritance):

缺点同构造函数

Javascript 相关文章推荐
ie 处理 gif动画 的onload 事件的一个 bug
Apr 12 Javascript
javascript实现二分查找法实现代码
Nov 12 Javascript
js 鼠标点击事件及其它捕获
Jun 04 Javascript
JQuery扩展插件Validate 3通过参数设置错误信息
Sep 05 Javascript
基于jquery的多功能软键盘插件
Jul 25 Javascript
浅析Js中的单引号与双引号问题
Nov 06 Javascript
基于jQuery实现的QQ表情插件
Aug 25 Javascript
javascript基本数据类型和转换
Mar 17 Javascript
angularjs实现table增加tr的方法
Feb 27 Javascript
使用vuex缓存数据并优化自己的vuex-cache
May 30 Javascript
对Vue- 动态元素属性及v-bind和v-model的区别详解
Aug 27 Javascript
node解析修改nginx配置文件操作实例分析
Nov 06 Javascript
基于JavaScript实现继承机制之原型链(prototype chaining)的详解
May 07 #Javascript
基于JavaScript实现继承机制之构造函数+原型链混合方式的使用详解
May 07 #Javascript
使用javascript:将其它类型值转换成布尔类型值的解决方法详解
May 07 #Javascript
JQuery+CSS提示框实现思路及代码(纯手工打造)
May 07 #Javascript
基于IE下ul li 互相嵌套时的bug,排查,解决过程以及心得介绍
May 07 #Javascript
解决javascript:window.close()在chrome,Firefox下失效的问题
May 07 #Javascript
jQuery的slideToggle方法实例
May 07 #Javascript
You might like
phpmyadmin 访问被拒绝的真实原因
2009/06/15 PHP
PHP调用MsSQL Server 2012存储过程获取多结果集(包含output参数)的详解
2013/07/03 PHP
解析php类的注册与自动加载
2013/07/05 PHP
CodeIgniter模板引擎使用实例
2014/07/15 PHP
ThinkPHP文件上传实例教程
2014/08/22 PHP
prototype.js的Ajax对象
2006/09/23 Javascript
让回调函数 showResponse 也带上参数的代码
2007/08/13 Javascript
创建公共调用 jQuery Ajax 带返回值
2012/08/01 Javascript
博客侧边栏模块跟随滚动条滑动固定效果的实现方法(js+jquery等)
2013/03/24 Javascript
JS实现图片横向滚动效果示例代码
2013/09/04 Javascript
javascript特殊用法示例介绍
2013/11/29 Javascript
javascript匀速运动实现方法分析
2016/01/08 Javascript
JavaScript的ExtJS框架中数面板TreePanel的使用实例解析
2016/05/21 Javascript
javascript实现用户点击数量统计
2016/12/25 Javascript
打造通用的匀速运动框架(实例讲解)
2017/10/17 Javascript
基于vue-ssr的静态网站生成器VuePress 初体验
2018/04/17 Javascript
微信{"errcode":48001,"errmsg":"api unauthorized, hints: [ req_id: 1QoCla0699ns81 ]"}
2018/10/12 Javascript
webpack安装配置与常见使用过程详解(结合vue)
2020/06/01 Javascript
vue自定义组件(通过Vue.use()来使用)即install的用法说明
2020/08/11 Javascript
[01:37]TI4西雅图DOTA2前线报道 VG拿下首胜教练357给出获胜秘诀
2014/07/10 DOTA
[00:58]他们到底在电话里听到了什么?
2017/11/21 DOTA
Python Socket传输文件示例
2017/01/16 Python
python基础学习之如何对元组各个元素进行命名详解
2018/07/12 Python
Python multiprocessing多进程原理与应用示例
2019/02/28 Python
Win10+GPU版Pytorch1.1安装的安装步骤
2019/09/27 Python
django中瀑布流写法实例代码
2019/10/14 Python
浅析Python+OpenCV使用摄像头追踪人脸面部血液变化实现脉搏评估
2019/10/17 Python
python使用OpenCV模块实现图像的融合示例代码
2020/04/10 Python
Python figure参数及subplot子图绘制代码
2020/04/18 Python
使用Python matplotlib作图时,设置横纵坐标轴数值以百分比(%)显示
2020/05/16 Python
新手学python应该下哪个版本
2020/06/11 Python
python实现一个简单RPC框架的示例
2020/10/28 Python
耐克中国官方商城:Nike中国
2018/10/18 全球购物
Nike挪威官网:Nike.com (NO)
2018/11/26 全球购物
工作决心书
2014/03/11 职场文书
购房意向书范本
2014/04/01 职场文书