论坛首页 Web前端技术论坛

JavaScript原型对象

浏览 9086 次
精华帖 (0) :: 良好帖 (1) :: 新手帖 (0) :: 隐藏帖 (0)
作者 正文
   发表时间:2012-12-12  

      什么是原型对象?

      “我们创建的每个函数都有一个prototype(原型)属性,这个属性是一个指针,指向一个对象,而这个对象的用途是包含可以由特定类型的所有实例共享的属性和方法。”

      --摘自人民邮电出版社出版的Nicholas C.Zakas著的《JavaScript高级程序设计》(第2版)

      上面提到的对象,就是原型对象,它的用途是“包含可以由特定类型的所有实例共享的属性和方法”。那么怎么才能访问到它呢?按照上面的说法,每个函数都有一个prototype 指针,这个指针指向了它的原型对象,所以我们可以通过函数的prototype属性来访问函数的原型对象。

      下面用程序来说明。

function SuperType(){}
 
document.write(SuperType.prototype);//[object Object]
 

      可见函数SuperType的确存在一个prototype属性,其的确是指向了一个对象。但这个对象真是原型对象吗?怎么证明?上面不是说原型对象的用途是“包含可以由特定类型的所有实例共享的属性和方法”,既然如此,那我们就来看这个对象是不是真的有这个功能。

function SuperType(){}
      
 SuperType.prototype.name = 'Sam';//在SuperType.prototype指向的对象上增加了name属性
  	
 //在SuperType.prototype指向的对象上增加了sayName方法
 SuperType.prototype.sayName = function() {
          document.write(this.name);
 };
 SuperType.prototype.setName = function(name) {
          SuperType.prototype.name = name;
 };
  	
 var instance1 = new SuperType();
 var instance2 = new SuperType();
  	
 instance1.sayName();//Sam
 instance2.sayName();//Sam
  	
 instance1.setName('Luce');
     
 instance1.sayName();//Luce
 instance2.sayName();//Luce
 

      当实例instance1调用setName()函数更改了SuperType.prototype.name的值后,instance2.sayName()也会输出Luce,说明SuperType.prototype指向的对象的确是“包含了可以由特定类型(这里是SuperType)的所有实例所共享的属性和方法”,所以由此可以得出结论:

      SuperType.prototype指向的就是SuperType的原型对象,也即是函数的prototype属性指向了函数的原型对象。 

 

 

 

      下面用程序来说明构造函数、实例和原型对象三者之间的关系。 

 

      首先是构造函数与实例之间的关系,实例就是通过调用构造函数创立的:

function SuperType(name){
           this.name = name;
           this.sayName = function(){
                 document.write(this.name);    
          }
 }
  	
 var instance1 = new SuperType("Sam");
 instance1.sayName();//Sam
  	
 var instance2 = new SuperType("Luce");
 instance2.sayName();//Luce
 

      我们在构造函数里面定义了一个name属性和一个sayName()函数,当调用new SuperType()创建实例时,就会调用构造函数SuperType创建实例对象,同时为实例对象增加一个name 属性和一个sayName()方法(也即是把this换成了实例对象(如this.name换成了instance1.name),所以每个实例的name和sayName()方法都是独立的,注意和在原型对象上定义的 区别,在原型对象上定义是共享的)。

     

      构造函数与原型对象的关系。前面已经说过,构造函数中有一个prototype属性,该属性是一个指针,指向了它的原型对象,那原型对象呢?是不是在它里面也存在一个指针,指向构造函数呢?答案是的确如此。原型对象中有一个constructor属性,该属性指向了构造函数:

function SuperType(){}
  	
document.write(SuperType.prototype.constructor);//function SuperType(){}
document.write(SuperType.prototype.constructor == SuperType);//true
 

      打印SuperType.prototype.constructor,输出的是构造函数的定义,而我们知道函数名只是一个引用,其指向函数所在的地址,所以通过SuperType.prototype.constructor和SuperType的比较,返回了true,所以SuperType.prototype.constructor和SuperType一样都指向了SuperType()函数,在这里即是指向了构造函数。

 

      最后是实例与原型对象的关系。前面的例子可以看到(最开始),实例可以访问到在原型对象上定义的属性和方法,那么我们就可以猜测,实例中肯定有一个指针指向了原型对象,的确如此,实例中有一个属性__proto__(IE中没有这个属性,所以不要在IE下测试),该属性指向了原型对象:

function SuperType(){}
  	
 var instance = new SuperType();
 document.write(instance.__proto__ == SuperType.prototype);//true

   

 

      SuperType.prototype指向了SuperType的原型对象,而instance.__proto__和SuperType.prototype比较返回了true,这就说明instance.__proto__也指向了SuperType的原型 对象。

 

     以上即是JavaScript原型对象的相关信息。

 

  • 大小: 3.1 KB
  • 大小: 2.6 KB
   发表时间:2012-12-13  
新手学习了。
0 请登录后投票
   发表时间:2012-12-13  
看下来很清晰,赞一个!
0 请登录后投票
   发表时间:2012-12-14  

为什么我的结果和你的不一样?

 function b(){};
	b.prototype.name='name1';
	b.prototype.sayName = function(){
		console.log(this.name);
	};
	b.prototype.setName = function(p){
		this.name = p;
	};
	
    var a = new b();
	var c  = new b();
	a.sayName();
	c.sayName();
	a.setName('wen');
	a.sayName();
	c.sayName();

  结果是:

 写道
name1
name1
wen
name1
 
0 请登录后投票
   发表时间:2012-12-14  
wenjianping007 写道

为什么我的结果和你的不一样?

 function b(){};
	b.prototype.name='name1';
	b.prototype.sayName = function(){
		console.log(this.name);
	};
	b.prototype.setName = function(p){
		this.name = p;
	};
	
    var a = new b();
	var c  = new b();
	a.sayName();
	c.sayName();
	a.setName('wen');
	a.sayName();
	c.sayName();

  结果是:

 写道
name1
name1
wen
name1
 

你看你写的和我写的一不一样?你在方法sayName()和setName()里面引用的是this.name,在这里this指向的是a和b对象,所以this.name访问的就是a.name或者b.name。就是说你这里this.name访问的是实例上的name属性,而不是prototype所指向的原型对象上的name属性。 Javascript有个特点,就是当实例调用某个属性的时候,它会先在实例中找,如果找到就返回,否则继续在原型对象上找,找到再放回。在这里,当你访问this.name的时候,由于没有在实例(a和b)中定义name属性,所以它就继续在原型对象上找,而原型对象上定义了name,所以第一次调用 a.sayName(); c.sayName(); 就会返回原型对象上定义的name属性,所以就返回了name1。而当你调用 a.setName('wen'); 的时候,this.name被换成了a.name,你实际上是在a对象上定义了name属性,而不是在原型对象上,所以该属性就不会是共享的,只有a所有,所以a.sayName()就会返回wen(因为实例中有name,就不再搜索原型对象了)。而最后调用c.sayName();因为c中没有name属性,所以它就到原型对象上找,所以就输出了原型对象上的name。
0 请登录后投票
   发表时间:2013-01-08  
好贴!很受用
0 请登录后投票
   发表时间:2013-03-11  
修补匠 写道
wenjianping007 写道

为什么我的结果和你的不一样?

 

 function b(){};
	b.prototype.name='name1';
	b.prototype.sayName = function(){
		console.log(this.name);
	};
	b.prototype.setName = function(p){
		this.name = p;
	};
	
    var a = new b();
	var c  = new b();
	a.sayName();
	c.sayName();
	a.setName('wen');
	a.sayName();
	c.sayName();

  结果是:

 

 写道
name1
name1
wen
name1
 

你看你写的和我写的一不一样?你在方法sayName()和setName()里面引用的是this.name,在这里this指向的是a和b对象,所以this.name访问的就是a.name或者b.name。就是说你这里this.name访问的是实例上的name属性,而不是prototype所指向的原型对象上的name属性。 Javascript有个特点,就是当实例调用某个属性的时候,它会先在实例中找,如果找到就返回,否则继续在原型对象上找,找到再放回。在这里,当你访问this.name的时候,由于没有在实例(a和b)中定义name属性,所以它就继续在原型对象上找,而原型对象上定义了name,所以第一次调用 a.sayName(); c.sayName(); 就会返回原型对象上定义的name属性,所以就返回了name1。而当你调用 a.setName('wen'); 的时候,this.name被换成了a.name,你实际上是在a对象上定义了name属性,而不是在原型对象上,所以该属性就不会是共享的,只有a所有,所以a.sayName()就会返回wen(因为实例中有name,就不再搜索原型对象了)。而最后调用c.sayName();因为c中没有name属性,所以它就到原型对象上找,所以就输出了原型对象上的name。

 

   继承自protype中的数据属性,只能Get不能put。

  

 

0 请登录后投票
   发表时间:2013-06-20  
JavaScript是一门强大的语言,可惜就是调试工具太弱了点。
0 请登录后投票
   发表时间:2013-06-21   最后修改:2013-06-21
0 请登录后投票
论坛首页 Web前端技术版

跳转论坛:
Global site tag (gtag.js) - Google Analytics