浏览 8739 次
锁定老帖子 主题:详解javascript类继承机制的原理
精华帖 (0) :: 良好帖 (0) :: 新手帖 (0) :: 隐藏帖 (0)
|
|
---|---|
作者 | 正文 |
发表时间:2007-09-03
最后修改:2011-03-11
本文着重解析javascript类继承机制,让你从底层了解javascript是怎样实现“继承”这一概念的。
jimichan在javaeye博客发布,转载请说明。
目前 javascript的实现继承方式并不是通过“extend”关键字来实现的,而是通过 constructor function和prototype属性来实现继承。首先我们创建一个animal 类 js 代码
构造函数与其他普通函数区别在于,1.构造函数里有 this关键字,2.调用构造函数是使用的new关键字。通过new运算符调用构造函数 animal 后,系统就会返回一个对象,这个对象就相当于 js 代码
到这里我们知道如何在js中定义一个类了,接下来我们展示如何写一个cat js 代码
到这里,cat就继承了 animal 对象,类cat的一个实例对象c1拥有属性name,age,height,和方法play了。 那么 prototype起到了一个什么样的作用呢? prototype就好比一个指针,它指向一个object,这个object就称为子类对象的原型。当cat的对象被创建的时候,由于cat的构造函数拥有prototype属性,那么cat的实例就会间接指向这个原型对象了(说成间接的是因为每个object都有一个 constructor 属性指向它的构造函数)。 那么问题来了,“当我们修改对象 c1 的name属性的时候,会不会修改它prototype的name属性值呢?”,答案是否定的。 接下来详细解析: 1.访问name属性: 首先当我们第一次访问c1.name的属性的时候,我们会得到值“pipi”,这个和我们预料中的一样。但是计算过程你未必知道。 它计算的过程是这样的:第一步:检查c1对象中是否有name属性,找到的话就返回值,没有就跳到第二步,显然没有找到,因为cat的构造函数中没有定义。第二步:当第一步没有找时,去间接访问 prototype对象所指向的object,如果在 prototype对象中 找到的name属性的话,就返回找到的属性值。如果还是没有找到的话,再去递归地寻找 prototype对象的 prototype对象(去找它的爷爷) ,一直到找到name属性或者没有prototype对象为止。如果到最后还是没有找到name属性的话就返回undefined。 2.设定name属性:当我们设定c1对象的name属性时,及调用 c1.name= ' new name'; 这个过程就简单多了。首先检查是否对象已有该属性,若已存在则修改当前值,若不存在则为该对象新增一个属性并设定当前值。值得一提的是,在设定值的过程中没有去访问 prototype属性。 为了加深理解,我们再看一个 read-write-read 的过程,第一次read的时候,由于自己的对象没有name属性,那么就会返回的原型对象的name属性的值。第二步,写入name的值,同样没发现本身对象有name属性,那么就在本身对象上新建一个name属性,然后赋值。第三步,再次读取name属性,由于在第二步中已经新建了name属性,此时就返回在第二步中设定的值。值得一提的是,在这三步中没有改变原型对象的值。 好了,到此详细分析了 javascript对象是如果实现继承的,其实和其他的面向对象语言不一样的是,javascript的继承机制是对象的原型继承而不是类型继承。 呵呵,欢迎看完,有不对的地方欢迎大家讨论! 声明:ITeye文章版权属于作者,受法律保护。没有作者书面许可不得转载。
推荐链接
|
|
返回顶楼 | |
发表时间:2007-09-04
不错,引用一下,能否讲一下原型模式呢?
|
|
返回顶楼 | |
发表时间:2007-09-04
引用 不错,引用一下,能否讲一下原型模式呢? javascript的原型机制和设计模式中的原型模式不一样,原型模式中使用对象clone技术,它是完全拷贝一个备份,而js的原型机制则是在运行时动态访问上层(或多层之上)的原型对象。原型模式最多只能使用到上一层对象的拷贝值。 |
|
返回顶楼 | |
发表时间:2007-09-04
引用 var = function(){ //这就是constructor function 了
是否掉了animal? |
|
返回顶楼 | |
发表时间:2007-09-04
呵呵,是的,谢谢提醒,一会补上
|
|
返回顶楼 | |
发表时间:2008-04-02
正好在看prototype所以搜到了楼主的代码。看了一下,自己也试验了。总结了一下也算稍微明白了其中的一些过程。补充几点东西把,经过试验,发现当你在New一个对象的时候,从这个操作的行为上来看,有2种可能的推断。第一种是他以你的构造函数也就是Cat对象为模版在内存里重新建立了一个新对象。并且将Cat中的属性注入到新的对象中,然后会将Cat的原型对象的construtor属性也注入到新对象中,因此你可以在新对象后面用“点”操作符点出construcor属性,它其实是指向Cat对象的。当你get新对象的属性时它首先在新对象内部寻找该属性,如果不存在,它会寻找constructor.prototype对象中是否有需要的属性,如果还是没有就继续递归直到根对象。而当你set的时候,因为c1.name被你动态的设进去了,所以再次get的时候自然是新对象本身的name属性,当然它是不会重写它原型对象中的name属性的。
第二种解释是,当你创造一个新对象时,它首先将prototype内的属性包括constructor属性copy到新对象里,然后对这个对象执行Cat里的this.name =xxx 这样新对象里的name属性自然被覆盖了,但是这种覆盖显然不会影响到原型的name,因为新对象本身只不过是原型的拷贝而已。其他的我想也没什么多说的了,如果有发现错误,可以在下面提出来,在讨论讨论。 |
|
返回顶楼 | |
发表时间:2008-04-02
不错的帖子!!!
在你上面说的一个问题!! "构造函数与其他普通函数区别在于,1.构造函数里有 this关键字,2.调用构造函数是使用的new关键字。" 我觉得这句话很值的商榷!!! 主要是构造函数里有 this关键字这句话,这个我看未必.一如果我选用空构造函数呢? var a=function(){}; a.prototype.firstName="hello"; a.prototype.secondName="world"; var b=new a(); alert(b.firstName); 二如下形式构造函数: var a=function(){ return { firstName:"Holle", secondName:"World" } } var b=new a(); alert(b.firstName); 这个难道里面具有this关键字吗??? 所以我觉得这里应该值得推敲!! |
|
返回顶楼 | |
发表时间:2008-04-07
正好在看prototype所以搜到了楼主的代码。看了一下,自己也试验了。总结了一下也算稍微明白了其中的一些过程。补充几点东西把,经过试验,发现当你在New一个对象的时候,从这个操作的行为上来看,有2种可能的推断。第一种是他以你的构造函数也就是Cat对象为模版在内存里重新建立了一个新对象。并且将Cat中的属性注入到新的对象中,然后会将Cat的原型对象的construtor属性也注入到新对象中,因此你可以在新对象后面用“点”操作符点出construcor属性,它其实是指向Cat对象的。当你get新对象的属性时它首先在新对象内部寻找该属性,如果不存在,它会寻找constructor.prototype对象中是否有需要的属性,如果还是没有就继续递归直到根对象。而当你set的时候,因为c1.name被你动态的设进去了,所以再次get的时候自然是新对象本身的name属性,当然它是不会重写它原型对象中的name属性的。
第二种解释是,当你创造一个新对象时,它首先将prototype内的属性包括constructor属性copy到新对象里,然后对这个对象执行Cat里的this.name =xxx 这样新对象里的name属性自然被覆盖了,但是这种覆盖显然不会影响到原型的name,因为新对象本身只不过是原型的拷贝而已。其他的我想也没什么多说的了,如果有发现错误,可以在下面提出来,在讨论讨论。 |
|
返回顶楼 | |
发表时间:2008-05-07
to 咖啡刀
感谢你的提醒。 那句话是表达的有问题。本意是指this关键字在构造函数里面会起到特定的作用。 构造函数不一定非要有this关键字。你可以试试在构造函数里面return一个非Function对象试试,js很有意思。 |
|
返回顶楼 | |
发表时间:2008-05-13
我也学了点,自己尝试写了下,但是感觉不是很好
不知道我说的对不对, 现在流行的框架prototype和。。。 他们的继承做的还不错, 但是给我的理解是,copy, 并非像C++那样的继承, 写法上很复杂, 真正去用继承去做项目的人不多, 大批的人在谈论这个, 但是实际效果不是很好 |
|
返回顶楼 | |