js 理解原型

1.理解原型

在js中,只要我们创建一个函数,系统就会自动为我们创建一个prototype属性,这个属性就指向函数的原型对象,在默认的情况下,自动的生成的原形对象都会有一个constructor的属性,这个属性指向prototype属性所在函数的指针。看下面代码:

<script type="text/javascript">   
    function Student()   
    {   
        alert(2);   
    }   
  
    alert(Student.prototype.constructor === Student);//true   
    Student.prototype.constructor();//2   
</script>  

 

上面的代码很简单。constructor是个默认属性,我们也可以扩展自定义属性和方法,这些扩展的属性和方法体现在每个实例对象上。看下面的代码:

<script type="text/javascript">   
    function Student()   
    {   
  
    }   
    //扩展原型   
    Student.prototype.name = "张三";   
    Student.prototype.score = 80;   
    Student.prototype.sayName = function ()   
    {   
        alert('我的名字是:'+this.name);   
    }   
    //创建两个新实例对象   
    var student1 = new Student();   
    var student2 = new Student();   
  
    student1.sayName();//我的名字是:张三   
    student2.sayName();//我的名字是:张三   
  
    //当我们改变构造函数的原型属性时,也会体现在每个实例上   
    Student.prototype.name = "李四";   
    student1.sayName();//我的名字是:李四   
    student2.sayName();//我的名字是:李四   
</script>  

 

上面的每个实例可以调用构造函数的原型对象的方法和属性是因为,在我们调用自定义的构造函数生成实例对象时,该实例的内部包含一个指针,指向构造函数的原型对象,在ECMA-262第5版中管这个指针叫[[Prototye]],这个属性是个内部属性,不可以访问。因为有[[Prototye]]属性,所以每个实例都可以访问其构造函数的原型里面的方法和属性。这属性连接了实例和构造函数的原型对象。下面的图片展示了实例和构造函数、构造函数原型之间的关系。
原型1
虽然[[Prototye]]是内部属性,为了能更好的了解内部原理Firefox、Safari和Chrome在每个实例对象上都支持一个属性__proto__,这个属性可以访问到构造函数的原型对象。看如下代码:

 
<script type="text/javascript">   
    function Student()   
    {   
  
    }   
    //扩展原型   
    Student.prototype.name = "张三";   
    Student.prototype.score = 80;   
    Student.prototype.sayName = function ()   
    {   
        alert('我的名字是:' + this.name);   
    }   
    //创建两个新实例对象   
    var student1 = new Student();   
    var student2 = new Student();   
           
    alert(student1.__proto__.name);//"张三"   
    alert(student1.__proto__ === Student.prototype);//true   
    alert(student2.__proto__===Student.prototype);//true   
</script>  

 

通过原型的这一特性我们可以了解到。 当所有的实例需要共享方法和属性时,可以把这些属性和方法添加在其构造函数的原型上。

2.实例访问原型对象

每个实例可以访问其构造函数的原型对象的属性和方法,实例虽然可以访问其属性和方法,但是不能通过实例来修改原型对象的属性和方法。看下面的代码:

<script type="text/javascript">   
    function Student()   
    {   
  
    }   
    //扩展原型   
    Student.prototype.name = "张三";   
    Student.prototype.score = 80;   
    Student.prototype.sayName = function ()   
    {   
        alert('我的名字是:' + this.name);   
    }   
    //创建两个新实例对象   
    var student1 = new Student();   
    var student2 = new Student();   
  
    student1.sayName();//我的名字是:张三   
    student2.sayName();//我的名字是:张三   
    //修改实例student1的name属性   
    student1.name = "李四";   
    student1.sayName();//我的名字是:李四   
    student2.sayName();//我的名字是:张三   
</script>  

 

上面的代码当我们修改student1的name属性时,并没有修改其构造函数的原型属性,其原因为实例可以访问其构造函数的原型属性和方法,是由于Javascript的作用域连向上查找机制,即先在自身查找,本身没有找到再在其指针指向的原型对象上查找。student1对象没有name属性,所以会到其指针指向的原型对象查找,就访问到name属性,当我们调用student1.name = "李四",根据JS的机制,对象自身没有name属性则会为对象添加name属性,如果自身存在则修改name属性的值,所以我们调用student1.name = "李四"时,则会为student1添加一个属性name,当下次访问时,按照查找机制,找到name='李四'就停止查找,对于student2呢还是以前的情况,本身没有,就会返回其[[Prototype]]指针指向的原型对象的name属性,即'张三'。其过程关系如下图:修改原型
为了更好的理解其过程,我们可以使用delete操作符删除其实例上的属性,那么实例应该可以访问到原型对象上的属性。看下面的代码:

function Student()   
{   
  
}   
//扩展原型   
Student.prototype.name = "张三";   
Student.prototype.score = 80;   
Student.prototype.sayName = function ()   
{   
    alert('我的名字是:' + this.name);   
}   
  
//创建两个新实例对象   
var student1 = new Student();   
var student2 = new Student();   
  
student1.sayName();//我的名字是:张三   
student2.sayName();//我的名字是:张三   
//修改实例student1的name属性   
student1.name = "李四";   
student1.sayName();//我的名字是:李四   
student2.sayName();//我的名字是:张三   
  
delete student1.name;   
student1.sayName();//我的名字是:张三   
student2.sayName();//我的名字是:张三  

 

当实例和其指针指向的原型对象拥有相同对象时,则返回的会是实例本身属性的值(实例本身属性和方法优先),自动屏蔽其指针指向的原型的值。还有就是虽然实例可以访问到其指针指向的原型对象的值,但却无法修改其值。

posted @ 2017-06-26 13:48  lcawen  阅读(124)  评论(0)    收藏  举报