前端开发必须知道的JS之原型和继承


Posted in Javascript onJuly 06, 2010

一. 原型与构造函数

Js所有的函数都有一个prototype属性,这个属性引用了一个对象,即原型对象,也简称原型。这个函数包括构造函数和普通函数,我们讲的更多是构造函数的原型,但是也不能否定普通函数也有原型。譬如普通函数:

function F(){ 
alert(F.prototype instanceof Object) //true; 
}

构造函数,也即构造对象。首先了解下通过构造函数实例化对象的过程。

function A(x){ 
this.x=x; 
} 
var obj=new A(1);

实例化obj对象有三步:

1. 创建obj对象:obj=new Object();

2. 将obj的内部__proto__指向构造他的函数A的prototype,同时,obj.constructor===A.prototype.constructor(这个是永远成立的,即使A.prototype不再指向原来的A原型,也就是说:类的实例对象的constructor属性永远指向"构造函数"的prototype.constructor),从而使得obj.constructor.prototype指向A.prototype(obj.constructor.prototype===A.prototype,当A.prototype改变时则不成立,下文有遇到)。obj.constructor.prototype与的内部_proto_是两码事,实例化对象时用的是_proto_,obj是没有prototype属性的,但是有内部的__proto__,通过__proto__来取得原型链上的原型属性和原型方法,FireFox公开了__proto__,可以在FireFox中alert(obj.__proto__);

3. 将obj作为this去调用构造函数A,从而设置成员(即对象属性和对象方法)并初始化。

当这3步完成,这个obj对象就与构造函数A再无联系,这个时候即使构造函数A再加任何成员,都不再影响已经实例化的obj对象了。此时,obj对象具有了x属性,同时具有了构造函数A的原型对象的所有成员,当然,此时该原型对象是没有成员的。

原型对象初始是空的,也就是没有一个成员(即原型属性和原型方法)。可以通过如下方法验证原型对象具有多少成员。

var num=0; 
for(o in A.prototype) { 
alert(o);//alert出原型属性名字 

num++; 
} 
alert("member: " + num);//alert出原型所有成员个数。

但是,一旦定义了原型属性或原型方法,则所有通过该构造函数实例化出来的所有对象,都继承了这些原型属性和原型方法,这是通过内部的_proto_链来实现的。

譬如

A.prototype.say=function(){alert("Hi")};

那所有的A的对象都具有了say方法,这个原型对象的say方法是唯一的副本给大家共享的,而不是每一个对象都有关于say方法的一个副本。

二. 原型与继承

首先,看个简单的继承实现。

function A(x){ 
this.x=x; 
} 
function B(x,y){ 

this.tmpObj=A; 

this.tmpObj(x); 

delete this.tmpObj; 

this.y=y; 
}

第5、6、7行:创建临时属性tmpObj引用构造函数A,然后在B内部执行,执行完后删除。当在B内部执行了this.x=x后(这里的this是B的对象),B当然就拥有了x属性,当然B的x属性和A的x属性两者是独立,所以并不能算严格的继承。第5、6、7行有更简单的实现,就是通过call(apply)方法:A.call(this,x);

这两种方法都有将this传递到A的执行里,this指向的是B的对象,这就是为什么不直接A(x)的原因。这种继承方式即是类继承(js没有类,这里只是指构造函数),虽然继承了A构造对象的所有属性方法,但是不能继承A的原型对象的成员。而要实现这个目的,就是在此基础上再添加原型继承。

通过下面的例子,就能很深入地了解原型,以及原型参与实现的完美继承。(本文核心在此^_^)

function A(x){ 
this.x = x; 
} 
A.prototype.a = "a"; 
function B(x,y){ 

this.y = y; 

A.call(this,x); 
} 
B.prototype.b1 = function(){ 

alert("b1"); 
} 
B.prototype = new A(); 
B.prototype.b2 = function(){ 

alert("b2"); 
} 
B.prototype.constructor = B; 
var obj = new B(1,3);

这个例子讲的就是B继承A。第7行类继承:A.call(this.x);上面已讲过。实现原型继承的是第12行:B.prototype = new A();

就是说把B的原型指向了A的1个实例对象,这个实例对象具有x属性,为undefined,还具有a属性,值为"a"。所以B原型也具有了这2个属性(或者说,B和A建立了原型链,B是A的下级)。而因为方才的类继承,B的实例对象也具有了x属性,也就是说obj对象有2个同名的x属性,此时原型属性x要让位于实例对象属性x,所以obj.x是1,而非undefined。第13行又定义了原型方法b2,所以B原型也具有了b2。虽然第9~11行设置了原型方法b1,但是你会发现第12行执行后,B原型不再具有b1方法,也就是obj.b1是undefined。因为第12行使得B原型指向改变,原来具有b1的原型对象被抛弃,自然就没有b1了。

第12行执行完后,B原型(B.prototype)指向了A的实例对象,而A的实例对象的构造器是构造函数A,所以B.prototype.constructor就是构造对象A了(换句话说,A构造了B的原型)。

alert(B.prototype.constructor)出来后就是"function A(x){...}" 。同样地,obj.constructor也是A构造对象,alert(obj.constructor)出来后就是"function A(x){...}" ,也就是说B.prototype.constructor===obj.constructor(true),但是B.prototype===obj.constructor.prototype(false),因为前者是B的原型,具有成员:x,a,b2,后者是A的原型,具有成员:a。如何修正这个问题呢,就在第16行,将B原型的构造器重新指向了B构造函数,那么B.prototype===obj.constructor.prototype(true),都具有成员:x,a,b2。

如果没有第16行,那是不是obj = new B(1,3)会去调用A构造函数实例化呢?答案是否定的,你会发现obj.y=3,所以仍然是调用的B构造函数实例化的。虽然obj.constructor===A(true),但是对于new B()的行为来说,执行了上面所说的通过构造函数创建实例对象的3个步骤,第一步,创建空对象;第二步,obj.__proto__ === B.prototype,B.prototype是具有x,a,b2成员的,obj.constructor指向了B.prototype.constructor,即构造函数A;第三步,调用的构造函数B去设置和初始化成员,具有了属性x,y。虽然不加16行不影响obj的属性,但如上一段说,却影响obj.constructor和obj.constructor.prototype。所以在使用了原型继承后,要进行修正的操作。

关于第12、16行,总言之,第12行使得B原型继承了A的原型对象的所有成员,但是也使得B的实例对象的构造器的原型指向了A原型,所以要通过第16行修正这个缺陷。

毕了。

Javascript 相关文章推荐
jQuery $.each的用法说明
Mar 22 Javascript
jQuery EasyUI 中文API Button使用实例
Apr 14 Javascript
JavaScript对象创建及继承原理实例解剖
Feb 28 Javascript
详解JavaScript ES6中的Generator
Jul 28 Javascript
基于zepto的移动端轻量级日期插件--date_picker
Mar 04 Javascript
javascript实现简易计算器的代码
May 31 Javascript
jQuery实现带延时功能的水平多级菜单效果【附demo源码下载】
Sep 21 Javascript
遍历json 对象的属性并且动态添加属性的实现
Dec 02 Javascript
JS作用域闭包、预解释和this关键字综合实例解析
Dec 16 Javascript
vuex页面刷新后数据丢失的方法
Jan 17 Javascript
微信小程序+云开发实现欢迎登录注册
May 24 Javascript
ionic3双击返回退出应用的方法
Sep 17 Javascript
基于jQuery的的一个隔行变色,鼠标移动变色的小插件
Jul 06 #Javascript
一个基于jquery的图片切换效果
Jul 06 #Javascript
jQuery ajax BUG:object doesn't support this property or method
Jul 06 #Javascript
防止页面被iframe(兼容IE,Firefox火狐)
Jul 04 #Javascript
JavaScript对象、属性、事件手册集合方便查询
Jul 04 #Javascript
JavaScript 内置对象属性及方法集合
Jul 04 #Javascript
js以对象为索引的关联数组
Jul 04 #Javascript
You might like
ThinkPHP无限级分类原理实现留言与回复功能实例
2014/10/31 PHP
yii2中LinkPager增加总页数和总记录数的实例
2017/08/28 PHP
CodeIgniter框架钩子机制实现方法【hooks类】
2018/08/21 PHP
PHP反射学习入门示例
2019/06/14 PHP
javascript String 的扩展方法集合
2008/06/01 Javascript
关闭ie窗口清除Session的解决方法
2014/01/10 Javascript
jQuery中:disabled选择器用法实例
2015/01/04 Javascript
Bootstrap Modal对话框如何在关闭时触发事件
2016/12/02 Javascript
浅谈angularjs $http提交数据探索
2017/01/20 Javascript
BootStrap 标题设置跨行无效的解决方法
2017/10/25 Javascript
详解vue为什么要求组件模板只能有一个根元素
2019/07/22 Javascript
js get和post请求实现代码解析
2020/02/06 Javascript
Vue记住滚动条和实现下拉加载的完美方法
2020/07/31 Javascript
Eclipse和PyDev搭建完美Python开发环境教程(Windows篇)
2016/11/16 Python
Python2包含中文报错的解决方法
2018/07/09 Python
Python判断两个文件是否相同与两个文本进行相同项筛选的方法
2019/03/01 Python
python 三元运算符使用解析
2019/09/16 Python
python生成器/yield协程/gevent写简单的图片下载器功能示例
2019/10/28 Python
Python 炫技操作之合并字典的七种方法
2020/04/10 Python
Python TestSuite生成测试报告过程解析
2020/07/23 Python
Python将字典转换为XML的方法
2020/08/01 Python
HTML页面中添加Canvas标签示例
2015/01/01 HTML / CSS
手对手的教你用canvas画一个简单的海报的方法示例
2018/12/10 HTML / CSS
世界第一曲奇连锁店:Mrs. Fields Cookies
2017/02/04 全球购物
德国自然时尚和有机产品购物网站:Waschbär
2019/05/29 全球购物
文员个人的求职信范文
2013/09/26 职场文书
师范生个人推荐信
2013/11/29 职场文书
宠物店的创业计划书范文
2014/01/11 职场文书
调解员先进事迹材料
2014/02/07 职场文书
统计工作个人总结
2015/03/03 职场文书
学校教学管理制度
2015/08/06 职场文书
家庭聚会祝酒词
2015/08/11 职场文书
导游词之贵州织金洞
2019/10/12 职场文书
基于Python绘制子图及子图刻度的变换等的问题
2021/05/23 Python
JPA如何使用entityManager执行SQL并指定返回类型
2021/06/15 Java/Android
Alexa停服!网站排名将何去何从?目前还没有替代品。
2022/04/15 杂记