关于模式:
Javascript编程提供三种模式,即工厂模式,构造函数模式,原型模式
工厂模式:最为简单粗暴的一种模式,格式如下:
functioncreatefactory(name,thing){
var o=new Object();
o.name=name;
o.thing=thing;
o.sayname=function(){
alert(this.name);
}
return o;
}
varfactory1=createfactory("A厂","java");
varfactory2=createfactory("B厂","javascript");
即创建一个函数,利用所有的对象都继承与Object这一个特点,在函数中创建一个Object对象,并填充Object的值,最后再将Object对象return出去,这种格式下,切记,由于createfactory是一个函数,而非所谓的构造函数,因此,在创建实例时切不可采用new!!(能new出来的都是构造函数,见下文)
构造函数模式:工厂模式升级版,在工厂模式中,每创建一个实例,我们总要显式地创建一个Object,对其升级,即可得到构造函数模式格式:
function Factory(name,thing){
this.name=name;
this.thing=thing;
this.sayname=function(){
alert(this.name);
}
}
var factory1=newFactory ("A厂","java");
var factory2=newFactory ("B厂","javascript");
此种模式下,采用了this指针,由于是构造函数,因此创建实例时需要采用new操作符。
在这种模式下,会涉及到一个问题,也就是new操作符干了些什么事?
事实上,new操作符会经历4步:
1. 创建一个新实例;
2. 将构造函数的作用域赋值给该新实例,也就是this指针将会指向这个新实例;
3. 执行构造函数中的代码;
4. 返回该新实例
这个时候,在每个新实例中都会保留一个constructor指针,用于指向该构造函数;
假如此时执行factory1.constructor== Factory,将会得到true!!
构造函数的问题?
由前面的new操作符我们可以获取出这样一个信息,每一个new,都将会在内存中开辟个空间,然后将构造函数中的内容放入该空间中,这本是无可厚非之事,但我们可以想象这样一种情况,假如每个实例中存在着一些相似的行为,我们这样做,是不是在浪费空间呢?比如这个例子中,每个实例都有sayname()这个方法,这个方法就是弹出该名字,这个行为是大家都共有的,那为啥需要在每个实例中都去创建这样一种方法呢?
由此引出第三种模式:原型模式
原型模式:
这里我们先明确一个概念,任何一个实例,都有构造函数与原型,
也就是说,实例实际上是从原型那里取出东西的,
前面的构造函数模式,是经历了[[prototype]]—> prototype—>原型的过程,([[prototype]]一般不可见,后来版本中将其引用为_proto_。)但是,我们一般还是认为,连接仍然是存在于实例与构造函数之间的,而不是存在于实例与构造函数之间。
好了,原型模式的格式为:
function Factory(){
}
factory.prototype.name="A厂"
factory.prototype.thing ="java"
factory.prototype.sayname =function(){
alert(this.name);
}
var factory1=new Factory();
好了,这个时候,所有东西都写进了原型了,所有的东西将会是共享的,这个时候的重复创建相同的东西的问题解决了,但也带来了另外一个问题,变量污染问题,任何一个实例,如果想要得到自己想要的东西,都要去操作原型,并把结果共享出去,这样是非常不好的,比如我说:
var factory1=new Factory();
var factory2=new Factory();
var factory1=new Factory();
alert(factory1.name);//A厂
factory2.name="B厂"
alert(factory2.name);//B厂
alert(factory1.name);//B厂
这样的操作,大概也很让人崩溃吧!!
那么图如何解决这问题呢?答案很明显,那就是结合两者之长喽,也就是说该共享的东西,我们把他写进原型,不该共享的东西,我们把他写进构造函数,大概格式如下:
function Factory(name,thing){
this.name=name;
this.thing=thing;
}
Factory.prototype={
constructor:Factory //采用这样写记得需要显示指定constructor,或者也可以采用下面的写法
sayname:function(){
alert(this.name);
}
}
Factory.prototype.sayname =function(){
alert(this.name);
}
var factory1=new Factory("A厂","java");
var factory2=new Factory("B厂","javascript");
这种构造函数与原型的混合模式,也是广泛采用的一种模式,既解决了内存重复占用的问题,也解决了变量共享的问题。
进一步优化?
虽然上面的方法已经很完美了,但这个时候我们从new操作符的第三个步骤中知道,会执行的操作,问题就出在,sayname()这个方法上,这是一个function函数,每次一执行到这里,暗地里都需要经过new Function(){}这么一个步骤,还是存在着一定的性能损耗。那么有什么方式可以解决这样一个问题呢?
我们可以做这样一个想法,假如我知道了sayname()已经是一个function()了,那么就直接执行alert(this.name);而不再创建一个new Function(){},如果不知道sayname()是一个function(),再执行new Function(){}这么一个步骤。因此代码就可以这样改了:
function Factory(name,thing){
this.name=name;
this.thing=thing;
//先判断再创建,此时需要在构造函数之中做处理
if(typeof this.sayname!="function"){
Factory.prototype.sayname=function(){
alert(this.name);
}
}
}
var factory1=new Factory("A厂","java");
var factory2=new Factory("B厂","javascript");
这种模式业界有这么一个说法,叫动态原型模式
其他模式:
寄生构造函数模式:主要解决一个在原有的一个原型的基础上添加功能又不修改原有原型,从这句话我们可以推断出,事实上得到的对象是原有对象的增强版,同时他既然可以做到不修改原有的,也就是说,得到的对象事实上与原来的对象将不存在任何关系,我们可以看这么一个例子:
function ZengqiangArray(){
var values=new Array();//values将具有普通Array所具有的功能
values.push.apply(values,arguments);
//对values做增强操作
values.toPipedString=function(){
returnthis.join("|");
};
returnvalues
}
var color=newZengqiangArray("red","yellow","green");
var bianhuacolor=color.toPipedString();
alert(bianhuacolor);
上述,我们在原先的数组格式的基础上改变了数组的输出格式,但是并没有去重写Array()方法。
这种方法在使用时慎用。
下篇将谈谈我对javascript中继承用法的理解。