原型真谛
正当我们感概万分时,天空中一道红光闪过,祥云中出现了观音菩萨。只见她手持玉净瓶,轻拂翠柳枝,洒下几滴甘露,顿时让JavaScript又添新的灵气。
观音洒下的甘露在JavaScript的世界里凝结成块,成为了一种称为“语法甘露”的东西。这种语法甘露可以让我们编写的代码看起来更象对象语言。
要想知道这“语法甘露”为何物,就请君侧耳细听。
在理解这些语法甘露之前,我们需要重新再回顾一下JavaScript构造对象的过程。
我们已经知道,用 var anObject = new aFunction() 形式创建对象的过程实际上可以分为三步:第一步是建立一个新对象;第二步将该对象内置的原型对象设置为构造函数prototype引用的那个原型对象;第三步就是将该对象作为this参数调用构造函数,完成成员设置等初始化工作。对象建立之后,对象上的任何访问和操作都只与对象自身及其原型链上的那串对象有关,与构造函数再扯不上关系了。换句话说,构造函数只是在创建对象时起到介绍原型对象和初始化对象两个作用。
那么,我们能否自己定义一个对象来当作原型,并在这个原型上描述类,然后将这个原型设置给新创建的对象,将其当作对象的类呢?我们又能否将这个原型中的一个方法当作构造函数,去初始化新建的对象呢?例如,我们定义这样一个原型对象:
var Person = //定义一个对象来作为原型类
{
Create: function(name, age) //这个当构造函数
{
this.name = name;
this.age = age;
},
SayHello: function() //定义方法
{
alert("Hello, I'm " + this.name);
},
HowOld: function() //定义方法
{
alert(this.name + " is " + this.age + " years old.");
}
};
这个JSON形式的写法多么象一个C#的类啊!既有构造函数,又有各种方法。如果可以用某种形式来创建对象,并将对象的内置的原型设置为上面这个“类”对象,不就相当于创建该类的对象了吗?
但遗憾的是,我们几乎不能访问到对象内置的原型属性!尽管有些浏览器可以访问到对象的内置原型,但这样做的话就只能限定了用户必须使用那种浏览器。这也几乎不可行。
那么,我们可不可以通过一个函数对象来做媒介,利用该函数对象的prototype属性来中转这个原型,并用new操作符传递给新建的对象呢?
其实,象这样的代码就可以实现这一目标:
function anyfunc(){}; //定义一个函数躯壳
anyfunc.prototype = Person; //将原型对象放到中转站prototype
var BillGates = new anyfunc(); //新建对象的内置原型将是我们期望的原型对象
不过,这个anyfunc函数只是一个躯壳,在使用过这个躯壳之后它就成了多余的东西了,而且这和直接使用构造函数来创建对象也没啥不同,有点不爽。
可是,如果我们将这些代码写成一个通用函数,而那个函数躯壳也就成了函数内的函数,这个内部函数不就可以在外层函数退出作用域后自动消亡吗?而且,我们可以将原型对象作为通用函数的参数,让通用函数返回创建的对象。我们需要的就是下面这个形式:
function New(aClass, aParams) //通用创建函数
{
function new_() //定义临时的中转函数壳
{
aClass.Create.apply(this, aParams); //调用原型中定义的的构造函数,中转构造逻辑及构造参数
};
new_.prototype = aClass; //准备中转原型对象
return new new_(); //返回建立最终建立的对象
};
var Person = //定义的类
{
Create: function(name, age)
{
this.name = name;
this.age = age;
},
SayHello: function()
{
alert("Hello, I'm " + this.name);
},
HowOld: function()
{
alert(this.name + " is " + this.age + " years old.");
}
};
var BillGates = New(Person, ["Bill Gates", 53]); //调用通用函数创建对象,并以数组形式传递构造参数
BillGates.SayHello();
BillGates.HowOld();
alert(BillGates.constructor == Object); //输出:true
[1] [2] [3] 下一页