当前位置: 代码迷 >> JavaScript >> 【原】关于JS中的原形prototype
  详细解决方案

【原】关于JS中的原形prototype

热度:219   发布时间:2012-10-10 13:58:11.0
【原】关于JS中的原型prototype

最近一直在看书。上班看。下班看。

关于原型一直不懂。

有的地方说是简单的复制。有的地方说是引用。

现在结合本人自己的理解以及测试的代码,给大家解释一下。

(如有说错。请大侠指出。看官别拍砖哈~~)

?

代码如下:

<script>
function a(){
  
}
a.prototype.sayHi = function(temp){
  alert(temp+" say HI")
}
function b(){

}
b.prototype = a.prototype;//为了证明prototype的赋值(b继承a)是复制(大家理解为复制,会好理解点。)
var A = new a;
A.sayHi("a");//调用sayHi方法。结果是a say HI
var B = new b;
B.sayHi("b")//调用sayHi方法。结果是b say HI(说明调用的方法一致)

A.sayHi = function(){
  alert("Hello world!")
}//大家应该都了解prototype的机制。会在原型链向上顺藤摸瓜到第一个方法。这里,我的目的就是改变当前对象的sayHi方法的引用(就是指向一个新的同名方法)。但是,原型链中的sayHi方法没变化。如果变化了,那么下面B.sayHi()肯定会跟随变化的。但事实却没有变化。
A.sayHi("a");//调用sayHi方法。结果是"Hello world
B.sayHi("b")//调用sayHi方法。结果是b say HI(说明调用的方法一致,证明了prototype的赋值(b继承a)的确是一个个的复制字面量)

a.prototype.goodbye = function(temp){
  alert(temp+" GoodBye");
}//这里,我在a类的原型链中晚绑定一个新方法。(记住JS的晚绑定机制!)
A.goodbye("a");//调用goodbye方法。结果是a GoodBye
B.goodbye("b");//调用goodbye方法。结果是b GoodBye(JS的晚绑定特性!!)
</script>

?

代码中的注释,我都是说的复制。

其实。JS中的对象包括Object和Function都是引用类型。

那为什么说是复制呢?

因为在第一次重写原型链中的sayHi方法的时候,是让A对象的sayHi方法重新引用到一个新的对象上。可是B对象的sayHi方法还是引用的内存中的原先的sayHi方法。这就是JS中鼎鼎大名的reference 。所以,代码中说的复制,其实更确切的说,应该是复制引用(我给这个机制起名为:references copy )!还是结合我的代码。其实2个sayHi方法都存在内存中,只是2个对象的指向不同而已。A的sayHi已经指向了新的改变了的方法,而B的还是指向原来的那个方法。这就想String类型的道理是一样的。

?

备注:

代码中的继承。我写的是b.prototype = a.prototype;

【备注:(这里是我后来看FF的监控,才知道的,上下两段代码是有区别的。感谢一位博友的指正。)】

请区别于b.prototype = new b;(js区别于java的特性还有:实例化类对象时,如果不传递参数,则可以省略括号。)

?

好了。就先解释这么多,本人的理解也有限。不到之处,请多多指正。

?

?

?

1 楼 maoquan0515 2011-03-25  
LZ举例不当导致理解错误!
请先理解下
b.prototype = a.prototype;  和

b.prototype = new a();的区别。

如果b.prototype = a.prototype,那么a的静态方法a.sayHi(区别于原型上的方法)是不会影响到b的,
对b.sayHi的调用过程是b.sayHi  --> b.prototype.sayHi(即 a.prototype.sayHi)--> ....

如果是b.prototype = new a();的话,
那么a.sayHi方法就在b的原型链上,

对b.sayHi的调用过程是b.sayHi--> b.prototype.sayHi --> a.sayHi(实例化的对象a) --> a.prototype.sayHi

PS:最好将构造函数名大写,对象名小写。



2 楼 北极的。鱼 2011-03-25  
maoquan0515 写道
LZ举例不当导致理解错误!
请先理解下
b.prototype = a.prototype;  和

b.prototype = new a();的区别。

如果b.prototype = a.prototype,那么a的静态方法a.sayHi(区别于原型上的方法)是不会影响到b的,
对b.sayHi的调用过程是b.sayHi  --> b.prototype.sayHi(即 a.prototype.sayHi)--> ....

如果是b.prototype = new a();的话,
那么a.sayHi方法就在b的原型链上,
对b.sayHi的调用过程是b.sayHi--> b.prototype.sayHi --> a.sayHi(实例化的对象a) --> a.prototype.sayHi



PS:最好将构造函数名大写,对象名小写。







楼上的。
请问你自己测试过代码吗?
我标记为红色的部分。
一个类的静态方法可以被这个类的实例调用吗?
连这个类自己的实例都不能访问的静态方法。
继承了这个类的类的实例又怎么能访问??
建议你仔细思考下。
||这里是重点,我们知道对象调用方法时首先检查实例方法,如果实例方法不存在,再去查找原型链,实例方法存在,直接调用实例方法。,前面A实现了sayHi的实例方法,所以直接输出18行的Hello world!,而不会再去原型链查找。而B方法仍然会去调用A的原型上的方法。这里如果将11行的b.prototype = a.prototype;改为b.prototype = new a();即b的原型为a的一个实例,那么17行重写a的实例方法sayHi 将会对B.sayHi("b")产生影响,请LZ调试!
  22.  
  23. a.prototype.goodbye = function(temp){ 
  24.   alert(temp+" GoodBye"); 
  25. }//这里,我在a类的原型链中晚绑定一个新方法。(记住JS的晚绑定机制!) 
  26. A.goodbye("a");//调用goodbye方法。结果是a GoodBye 
  27. B.goodbye("b");//调用goodbye方法。结果是b GoodBye(JS的晚绑定特性!!)
||如前面所述,由于B调用的是a的原型链,所以对a原型链的修改会影响到B,这是JS的惰性绑定没有问题
  28. </script> 
3 楼 maoquan0515 2011-03-25  
楼上的。
请问你自己测试过代码吗?
我标记为红色的部分。
一个类的静态方法可以被这个类的实例调用吗?
连这个类自己的实例都不能访问的静态方法。
继承了这个类的类的实例又怎么能访问??
建议你仔细思考下。


这个我描述有误,不是静态方法,被大小写搞晕了。我重新讲下。
你这里举例有问题,你描述的问题是对的,结论是错误,我引用你的代码,加上我自己的注释:

   1. <script> 
   2. function a(){ 
   3.    
   4. } 
   5. a.prototype.sayHi = function(temp){ 
   6.   alert(temp+" say HI") 
   7. } 
   8. function b(){ 
   9.  
  10. } 
  11. b.prototype = a.prototype;//为了证明prototype的赋值(b继承a)是复制(大家理解为复制,会好理解点。)   ||这里b的原型直接指向了a的原型,而不是指向a的具体对象
  12. var A = new a; 
  13. A.sayHi("a");//调用sayHi方法。结果是a say HI 
  14. var B = new b; 
  15. B.sayHi("b")//调用sayHi方法。结果是b say HI(说明调用的方法一致) 
  16.  ||上面两个方法A.sayHi("a");和 B.sayHi("b")其实均调用了a.prototype上的方法

  17. A.sayHi = function(){ 
  18.   alert("Hello world!") 
  19. }// 大家应该都了解prototype的机制。会在原型链向上顺藤摸瓜到第一个方法。这里,我的目的就是改变当前对象的sayHi方法的引用(就是指向一个新的同名方法)。但是,原型链中的sayHi方法没变化。如果变化了,那么下面B.sayHi()肯定会跟随变化的。但事实却没有变化。 ||这里重写了A的实例方法sayHi

  20. A.sayHi("a");//调用sayHi方法。结果是"Hello world 
  21. B.sayHi("b")//调用sayHi方法。结果是b say HI(说明调用的方法一致,证明了prototype的赋值(b继承a)的确是一个个的复制字面量) 

||这里是重点,我们知道对象调用方法时首先检查实例方法,如果实例方法不存在,再去查找原型链,实例方法存在,直接调用实例方法。,前面A实现了sayHi的实例方法,所以直接输出18行的Hello world!,而不会再去原型链查找。而B方法仍然会去调用A的原型上的方法。这里如果将11行的b.prototype = a.prototype;改为b.prototype = new a();即b的原型为a的一个实例,那么17行重写a的实例方法sayHi 将会对B.sayHi("b")产生影响,请LZ调试!

===============================================

这里b.prototype=A;而不是new a();
这条赋值语句放在A实例化后,对A的修改会影响到B

================================================

楼主调试一下下面代码:
function a(){
 
}
a.prototype.sayHi = function(temp){
   alert(temp+" say HI")
}
function b(){

}
b.prototype =A;  //对比 b.prototype = a.prototype!!

A = new a();
B = new b();

A.sayHi = function(){ 
   alert("Hello world!") 
}

B.sayHi();
4 楼 maoquan0515 2011-03-25  
不好意思,刚才那段话有错误。我不会在javaeye里修改评论。

有问题的话:
=====================================

||这里是重点,我们知道对象调用方法时首先检查实例方法,如果实例方法不存在,再去查找原型链,实例方法存在,直接调用实例方法。,前面A实现了sayHi的实例方法,所以直接输出18行的Hello world!,而不会再去原型链查找。而B方法仍然会去调用A的原型上的方法。这里如果将11行的b.prototype = a.prototype;改为b.prototype = new a();即b的原型为a的一个实例,那么17行重写a的实例方法sayHi 将会对B.sayHi("b")产生影响,请LZ调试!

===============================================

这里b.prototype=A;而不是new a();
这条赋值语句放在A实例化后,对A的修改会影响到B

================================================

楼主调试一下下面代码:
function a(){
 
}
a.prototype.sayHi = function(temp){
   alert(temp+" say HI")
}
function b(){

}
b.prototype =A;  //对比 b.prototype = a.prototype!!

A = new a();
B = new b();

A.sayHi = function(){ 
   alert("Hello world!") 
}

B.sayHi();




=====================================
我很高兴你这么专业。
不过,希望楼上的兄弟放出代码前,自己先测试下。
我清楚你想表达的。代码如下:
<script>
function a(){
}
a.prototype.sayHi = function(temp){
alert(temp+" say HI")
}
A = new a();
function b(){
}
b.prototype =A;
B = new b();
A.sayHi = function(){
alert("Hello world!")
}
B.sayHi();
</script>

首先。我要说明一下。
你的代码运行达到了你的预期效果。
即,调用B.sayHi();改变了a.prototype中定义的sayHi方法了。
可是这不能说明是改变了原型中的sayHi。
B中调用这个方法只是搜索到A(a的实例)中的的静态方法,就停止搜索了。
仔细想想,是不是这样的?
为了证明这一点,我写了如下代码。(仅仅是在最后添加了一个c。绿色标记出)
function a(){
}
a.prototype.sayHi = function(temp){
alert(temp+" say HI")
}
A = new a();
function b(){
}
b.prototype =A;
B = new b();
A.sayHi = function(){
alert("Hello world!")
}
B.sayHi();
var c = new a;
c.sayHi("c")


结果显而易见。
证明了其实原型链中的sayHi方法被没有被重写。
简言之,还是返回到JS中的原型链机制。一个方法被调用的时候,只是顺藤摸瓜搜索第一个同名的方法。如你说的,先搜自己的静态成员,然后是自己的prototype。然后是父类的静态成员,然后是父类的prototype。如此顺藤摸瓜上去,知道Object的prototype为止!我在原文中给这个机制起了个名字叫references copy。
可是,实质是怎么样的呢?当一个类或实例重新设置同名的方法的时候,其实是把这个类或实例的这个属性指向新的引用了。原来的prototype中的同名的方法并没有被冲掉。
希望能对你有帮助。
||这里是重点,我们知道对象调用方法时首先检查实例方法,如果实例方法不存在,再去查找原型链,实例方法存在,直接调用实例方法。,前面A实现了sayHi的实例方法,所以直接输出18行的Hello world!,而不会再去原型链查找。而B方法仍然会去调用A的原型上的方法。这里如果将11行的b.prototype = a.prototype;改为b.prototype = new a();即b的原型为a的一个实例,那么17行重写a的实例方法sayHi 将会对B.sayHi("b")产生影响,请LZ调试!

===============================================

这里b.prototype=A;而不是new a();
这条赋值语句放在A实例化后,对A的修改会影响到B

================================================

楼主调试一下下面代码:
function a(){
 
}
a.prototype.sayHi = function(temp){
   alert(temp+" say HI")
}
function b(){

}
b.prototype =A;  //对比 b.prototype = a.prototype!!

A = new a();
B = new b();


A.sayHi = function(){ 
   alert("Hello world!") 
}

B.sayHi();



我很高兴你这么专业。
不过,希望你在提交代码前,先自己运行下代码。正确了再提交。
看我绿色处。
首先你把一个对象赋给了b类的原型。正确,可以这么做。(不过,请记得在实例化A以后!!)
然后
maoquan0515 写道
不好意思,刚才那段话有错误。我不会在javaeye里修改评论。

有问题的话:
=====================================

5 楼 北极的。鱼 2011-03-25  
maoquan0515 写道
不好意思,刚才那段话有错误。我不会在javaeye里修改评论。

有问题的话:
=====================================

6 楼 maoquan0515 2011-03-25  
好吧,直奔主题。我整理下你的思路,首先定义a,b两个构造函数,,b的原型指向a的原型,然后你重写了a的实例方法,这时候a弹出"Hello world!",b弹出"b say HI",由此你认为“证明了prototype的赋值(b继承a)的确是一个个的复制字面量”

这里面的问题是:
b.prototype = a.prototype;使b的原型指向a的原型,你重写了a的实例方法并不会影响到a原型上的方法,这时候b通过原形链仍然指向a的原型,a的原型方法也仍然还是alert(temp+" say HI"),只不过是a的实例方法覆盖了其原型方法。
请问这和b的复制云云的有什么关系????b仍然指向a的原型,无论a的实例方法写成什么对原型都没有影响,因为b总是执行a.prototype.sayHi。

你的文章中有些地方需斟酌:
1.
引用
代码中的继承。我写的是b.prototype = a.prototype;

其实还可以写成b.prototype = new b;


你认为这两个一样吗??

2.
引用
简言之,还是返回到JS中的原型链机制。一个方法被调用的时候,
只是顺藤摸瓜搜索第一个同名的方法。如你说的,先搜自己的静态成员,
然后是自己的prototype。然后是父类的静态成员,然后是父类的prototype。


静态方法、实例方法、原型方法你搞清楚了吗???原型链中会搜索静态方法吗?

3.

b.prototype =A;  //对比 b.prototype = a.prototype!!

我写这两个的对比只是想告诉你,如果你使用b.prototype = a.prototype实现b继承a的话,你对a实例方法的修改将不会影响到b的任何方法,除非你改变a原型方法,这样会影响到b。而你使用b.prototype =A的话,对具体实例A的修改则会影响到b,仅此而已。

4.这里的大小写真的搞的我头晕,具体实例用大写,构造函数用小写,楼主需要专业一点。

5.这篇文章错误百出,“顶”的人可能就楼主一个吧??

6。结束,不来了,怎么撞到这里来的,晕死。
7 楼 北极的。鱼 2011-03-28  
maoquan0515 写道
好吧,直奔主题。我整理下你的思路,首先定义a,b两个构造函数,,b的原型指向a的原型,然后你重写了a的实例方法,这时候a弹出"Hello world!",b弹出"b say HI",由此你认为“证明了prototype的赋值(b继承a)的确是一个个的复制字面量”

这里面的问题是:
b.prototype = a.prototype;使b的原型指向a的原型,你重写了a的实例方法并不会影响到a原型上的方法,这时候b通过原形链仍然指向a的原型,a的原型方法也仍然还是alert(temp+" say HI"),只不过是a的实例方法覆盖了其原型方法。
请问这和b的复制云云的有什么关系????b仍然指向a的原型,无论a的实例方法写成什么对原型都没有影响,因为b总是执行a.prototype.sayHi。

你的文章中有些地方需斟酌:
1.
引用
代码中的继承。我写的是b.prototype = a.prototype;

其实还可以写成b.prototype = new b;


你认为这两个一样吗??

2.
引用
简言之,还是返回到JS中的原型链机制。一个方法被调用的时候,
只是顺藤摸瓜搜索第一个同名的方法。如你说的,先搜自己的静态成员,
然后是自己的prototype。然后是父类的静态成员,然后是父类的prototype。


静态方法、实例方法、原型方法你搞清楚了吗???原型链中会搜索静态方法吗?

3.

b.prototype =A;  //对比 b.prototype = a.prototype!!

我写这两个的对比只是想告诉你,如果你使用b.prototype = a.prototype实现b继承a的话,你对a实例方法的修改将不会影响到b的任何方法,除非你改变a原型方法,这样会影响到b。而你使用b.prototype =A的话,对具体实例A的修改则会影响到b,仅此而已。

4.这里的大小写真的搞的我头晕,具体实例用大写,构造函数用小写,楼主需要专业一点。

5.这篇文章错误百出,“顶”的人可能就楼主一个吧??

6。结束,不来了,怎么撞到这里来的,晕死。




首先。请你审视下,你现在的论点和原先的不一样。
原先,你想表明父类的原型里的内容是可以被改变的。(看你的例子。)
现在,你却又说父类的原型里的内容不能被改变。
你真的看懂我的意思吗?请你仔细看看我的原文。
   总结1:    b.prototype=a.prototype;
                  b.prototype=A;//这2种继承的区别你研究过吗?我说的b的实例调用方法的时候会有一个搜索父类的静态同名方法的过程,特指的是子类的prototype是父类的一个实例的情况下。如果子类的prototype是父类的prototype,另当别论!(我举的这个例子,你真的看懂了吗?!!
   总结2:    你一直强调是子类的方法是覆盖!(请看你自己的评论!)
                  我给你写的最后一段代码,已经证明了,只是引用重新定向而已。事实说明一切。而你自己也知道你的解释站不住。还在一个劲的指责别人的理论。(我放出的代码全是运行过的!你的呢?!我不修改,根本无法运行!我不是指责你,只是想说,请你放出代码前自己先测试。)你一直问,复制是什么意思,我在博文里说了很清楚了,是引用复制!OK?!你还不理解,就指责错误。和你说了这么多,真浪费时间!!
  相关解决方案