JavaSctipt之prototype原型深入分析: prototype的属性是副本,引用,还是固定的查找方式?

JavaSctipt之prototype原型深入分析: prototype的属性是副本,引用,还是固定的查找方式?


//

JavaSctipt之prototype原型是什么原理呢?实例被创建时,它是复制原型,得到原型属性的副本,还是使实例的属性引用原型的属性值,

还是实例被创建时,根本任何没有与原型同名的属性,仅靠"向原型查找"这种固定方式访问原型呢?


请看例子分析:

 01_修改prototype的属性

  02_增加prototype的属性

  03_向实例增加prototype的同名属性



---------------------------------------------------------------------------------------------------------华丽的分分隔线---------------------------------------------------------------------------------------------------------


JavaSctipt之prototype原型深入分析: prototype的属性是副本,引用,还是固定的查找方式?
                                      ----01_修改prototype的属性
//
  	<script type="text/javascript">
  
  	var Person  = function()
  	{
  	};
  	
  	Person.prototype.username = new String("zhangsan");
  	
  	var person1 = new Person();
  	var person2 = new Person();
  	
  	alert("实例创建后,person1.username: "+person1.username);
  	alert("实例创建后,person2.username: "+person2.username);
  	
  	
  	//修改原型的属性值:
  	Person.prototype.username = new String("lisi");
  	
  	
  	alert("原型被修改后,person1.username: "+person1.username);
  	alert("原型被修改后,person2.username: "+person2.username);
  	
  	
  	
  	</script>
  	


//

输出:

实例创建后,person1.username: zhangsan
实例创建后,person2.username: zhangsan

原型被修改后,person1.username: lisi
原型被修改后,person2.username: lisi

 

    
问题: 为什么原型的属性值被修改后,2个实例person1,person2的属性值也变了呢?
    
假设1副本: 
    N个实例创建时都持有原型的属性的副本,原型的属性被修改后,接着进行修改N个实例上的属性值(进行N次修改).
这样在原型的属性值改变后,同时修改实例上的副本的值.
   --要进行N次修改,这个应该不太可能吧!
   
假设2引用: 
    N个实例创建时都持有原型的属性的引用,此时Person.prototype.username 和 person.username 都是引用同一个对象String("zhangsan"),当Person.prototype.username = new String("lisi")执行后,Person.prototype.usename引用新的对象String("lisi"),接着修改N个实例上的username属性,使其username也引用同一个对象:String("lisi").
这样原型的属性引用新对象后,实例的属性也引用同一个新对象.
        --要进行N次修改,这个可能性也不高.
 

假设3固定查找方式:
    N个实例创建时没有原型同名的属性,如person创建时,它自身并没有username属性! 当访问person.username时,因为它自身并没有username属性,所以按照固定的查找方式来查找: 
      因为person是Person的实例(person instanceof Person 得到的是true),所以查找Person.prototype的属性,如果没找到,继续向上一层原型查找,在这里,js引擎找到了Person.prototype.username属性,故输出!

因为这个查找方式,当修改原型的属性值Person.prototype.username后,并不需要进行额外的操作!
所以原型的属性值被修改为"lisi"后,person1.username查找到的也是原型的属性值!
  --仅需修改prototype,然后按固定查找方式查找,能解译此例!



---------------------------------------------------------------------------------------------------华丽的分分隔线-----------------------------------------------------------------------------------------------------





JavaSctipt之prototype原型深入分析: prototype的属性是副本,引用,还是固定的查找方式?
                                      ----02_增加prototype的属性

//

<script type="text/javascript">
  
  	var Person  = function()
  	{
  	};
  	
  	Person.prototype.username = "zhangsan";
  	
  	var person1 = new Person();
  	var person2 = new Person();
  	
  	alert("实例创建后,person1.username: "+person1.username);
  	alert("实例创建后,person2.username: "+person2.username);
  	
  	alert("向原型增加属性age前,person1.age: "+person1.age);
  	alert("向原型增加属性age前,person2.age: "+person2.age);
  	
  	//向原型增加属性
  	Person.prototype.age = new String("18");
  	
  	
  	alert("向原型增加属性age后,person1.age: "+person1.age);
  	alert("向原型增加属性age后,person2.age: "+person2.age);
  	
  	
  	</script>


//


输出:


 实例创建后,person1.username: zhangsan
 实例创建后,person2.username: zhangsan
 
向原型增加属性age前,person1.age: undefined
向原型增加属性age前,person2.age: undefined


向原型增加属性age后,person1.age: 18
向原型增加属性age后,person2.age: 18





问题: 为什么向原型的增加属性后,2个实例person1,person2也增加了对应的属性呢?
 
假设1副本: N个实例创建时都持有原型的属性的副本,向原型增加age属性后,接着向N个实例都增加age属性,且值与原型一样(副本)

    --要进行N次操作,可能性不高!
 
假设2引用: N个实例创建时都持有原型的属性的引用,向原型增加age属性后,接着向N个实例都增加age属性(引用),此时Person.prototype.age 和 person.age引用同一个对象String("18")

                     --要进行N次操作,可能性不高!
 

假设3固定查找方式:

N个实例创建时没有原型同名的属性,如person创建时,它自身并没有username属性!向原型增加age属性后,N个实例上同样没有age属性,当访问person1.age时,因为它自身并没有age属性,所以按照固定的查找方式来查找: 

因为person1是Person的实例(person1 instanceof Person 得到的是true),所以查找Person.prototype.age的属性,如果没找到,继续向上一层原型查找,在这里,js引擎找到了Person.prototype.age属性,故输出!

--仅需修改prototype,然后按固定查找方式查找,能解译此例!

                   



---------------------------------------------------------------------------------------------------华丽的分分隔线--------------------------------------------------------------



JavaSctipt之prototype原型深入分析: prototype的属性是副本,引用,还是固定的查找方式?
                                      ----03_向实例增加prototype的同名属性


//

  	<script type="text/javascript">
  
  	var Person  = function()
  	{
  	};
  	
  	Person.prototype.username = new String("zhangsan");
  	
  	var person = new Person();
  	
  	alert("实例创建后,person.username: "+person.username);  	
  	
  	//向实例增加与prototype同名的属性
  	person.username = new String("lisi");
  	
  	alert("向实例增加与prototype同名的属性后,person.username: "+person.username);  
  	
  	//删除实例上的username属性,然后访问[实例].username得到的会是undefined吗?
  	delete person.username;
  	
  	alert("删除实例上的username属性后,person.username: "+person.username);
  	
  	
  	</script>


//



 输出:


实例创建后,person.username: zhangsan
 
向实例增加与prototype同名的属性后,person.username: lisi

删除实例上的username属性后,person.username: zhangsan 





问题: 向实例增加与prototype同名的属性后(person.username = new String("lisi")),然后又删除该属性后,再次访问person.username,为什么得到的不是undefined,而是与原型相同的属性值呢?
 
假设1副本: 

       person实例创建时持有原型的属性的副本,然后将实例的username修改为String("lisi"),当删除person.username属性后
又同时向实例增加原型的属性的副本username=new String("zhangsan"),所以再次访问person.username的值也是"zhangsan"..

--删除person的username属性后又向person增加属性值为String("zhangsan")副本的username属性,显然不太可能嘛!
 

假设2引用: 

       person实例创建时持有原型的属性的引用,此时Person.prototype.username 和 person.username 都是引用同一个对象String("zhangsan"),person.username = new String("lisi")执行后,person.username引用新的对象 String("lisi"),当删除person.username属性后,又同时将person.username重新引用Person.prototype.username引用的对象String("zhangsan")!

            --删除person的username后,又同时向person增加引用String("zhangsan")的username属性,显然不太可能!

            

假设3固定查找方式:

person实例创建时没有与原型同名的username属性,person.username = new String("lisi")表示向person实例增加与原型同名的username属性,它引用新对象String("lisi"),此时Person.prototype.username引用对象String("zhangsan"),而person.username则引用String("lisi"),此时访问person.username时,因为person自己都有username属性,所以直接输出lisi,当delete person.username后再访问person.username,因为它自身并没有username属性,所以按照固定的 查找方式来查找: 因为person是Person的实例(person instanceof Person 得到的是true),所以查找Person.prototype的属性,如果没找到,继续向上一层原型查找, 在这里,js引擎找到了Person.prototype.username属性,故输出!

--就是当person.username属性存在时,它就会"屏蔽"了原型的username属性,删除person.username后,原型的username"屏蔽"解除,比较合理!


---------------------------------------------------------------------------------------------------华丽的分分隔线-----------------------------------------------------------------------------------------------------


总结:从自身延原型链向上查找的方式

从自身延原型链向上查找的方式:  

访问对象object.xxx的属性时,如果object自己没有xxx属性,则向上延原型链查找,如果找到,则输出,没找到,则输出undefined.

PS: 因为每个对象和原型都有一个原型(注:原型也是一个对象 ),对象的原型指向对象的父,而父的原型又指向父的父,我们把这种通过原型层层连接起来的关系撑为原型链。这条链的末端一般总是默认的对象原型。

JavaSctipt之prototype原型深入分析: prototype的属性是副本,引用,还是固定的查找方式?_第1张图片


如图,var person = new Person() 后,得到的person实例 与 原型Person.prototype 是这样的关系,初始时,person并没有username或age的属性! person.username执行时,因为自身没有username属性, 所以通过向上查找原型的属性得到的!


JavaSctipt之prototype原型深入分析: prototype的属性是副本,引用,还是固定的查找方式?_第2张图片
看图就明白了,关键是:原型的.xxx属性 与 实例.xxx属性 是两个不同的变量,不过是同名而已!

执行person.username = new String("lisi")后,person得到了username属性,既然person得到了username属性,所以没必要向原型查找了,此时alert(person.username)输出是lisi,而alert(person.age)还是要向原型查找,输出的是18....


原型真正魅力体现在多个实例共用一个通用原型的时候。原型对象(注:也就是某个对象的原型所引用的对象 )的属性一旦定义,就可以被多个引用它的实例所继承(注:即这些实例对象的原型所指向的就是这个原型对象 ),这种操作在性能和维护方面其意义是不言自明的!


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

PS:  下面,我们来看看什么是原型链?  <<原型链深入分析>>

你可能感兴趣的:(function,object,String,prototype,delete,js引擎)