基于原型的创建
JavaScript是一种基于原型的编程语言。在JavaScript中,每个构造函数都有与之关联的原型对象。这个原型对象的属性和方法表现出来就是用构造函数创建的每个对象的属性和方法。因此,可以通过将函数作为原型对象的属性分配给构造函数来创建方法。
第一眼看过去,觉得这个方法很像构造函数内的自定义方法,但是基于原型的方法创建有一些自己的优点。
- 因为函数只存储一次(在原型对象中),而不是在每个新的对象中都存储一次,所以这种方法比在构造函数中定义方法更加有效;
- 因为可以在任何时候修改原型对象,所以使用这个方法还可以向不是自己创建的那些类中添加方法(如html元素);
- 因为原型对象的变化会影响已经存在的对象,所以借助这种方法可以为创建出来的对象再添加方法。
当对不是自己创建的那些对象进行处理时,这些优点显得更加重要。例如,对JavaScript的String构造函数不能加以修改,因为它深深嵌在浏览器应用程序代码中,但是通过访问String的原型对象,却可以添加新的方法,并且脚本中那些String对象也可以访问那些新的方法。
一旦声明了一个构造函数,那么就可以通过它的prototype属性来访问原型对象:
function Bird()
{
this.feet =2;
this.feathers =true;
return true;
}
Bird.prototype.getFeetNum =getFeetNum;
function getFeetNum()
{
return this.feet;
}
当getFeetNum已经作为原型对象的一个属性赋给了Bird,那么Bird的每个对象就都可以调用该方法了:
var tweety =new Bird();
var numFeet =tweety.getFeetNum();
为了减少代码的混乱,可以直接把该函数创建为原型对象的一个属性:
function Bird()
{
this.feet =2;
this.feathers =true;
return true;
}
Bird.prototype.getFeetNum =function()
{
return this.feet;
}
JavaScript允许在源文件内的任何一个地方执行全局作用域的函数,不管函数是执行点之前还是执行点之后都可以。像下面这样一段代码总是可以工作的:
init();
function init()
{
.....
}
但是用prototype属性创建对象方法时,只有在将该方法添加到原型对象中去之后才可以调用它。这意味着下面这段代码不能工作:
var tweety =new bird();
var numFeet =tweety.getFeet();
function Bird()
{
....
}
Bird.prototype.getFeetNum =function()
{
....
};
上面这段代码中对tweety.getFeetNum的调用将会导致错误发生,因为getFeetNum尚未添加到Bird的原型对象中去。从上面的代码可以看到,可以在将方法添加到原型中去之前实例化对象:
var tweety =new Bird();
function Bird()
{
....
}
Bird.prototype.getFeetNum =function()
{
....
};
var numFeet =tweety.getFeet();
只要向原型对象添加函数的语句在调用那些函数之前,代码就可以正常工作。
这是在对象构造函数内部声明方法的优点之一,不管将构造函数放在代码文件的什么地方,都可以正常工作。正是由于这个原因,很多开发人员只有在不能修改构造函数而又需要对内嵌的类进行扩展的时候才选择使用原型。
讨论:
为内嵌的对象(如字符串)创建方法也可以使用prototype属性。例如,可以创建一个函数,用于将一个字符串值中的下划线转换为空格:
String.prototype.converUnderscores =function()
{
return this.replace(/ /g," ");
};
以上的语句执行后,每个字符串(不管是新创建的还是原来就有的)就都可以访问方法convertUnderscores了:
var underscored ="Are there any spaces in here? ";
var spaced =underscored.convertUnderscores();
变量spaced的值将为“Are there any spaces in here?”。