原型模式创建对象也有自己的缺点,它省略看构造函数传参初始化这一过程,带来的缺点就是初始化的值都是一致的。
而原型最大的缺点就是它优点,那就是共享。
原型中所有属性是被很多实例共享的,共享对于函数非常合适,对于包含基本值的属性也还可以,但如果属性包含引用类型。
就存在一定的问题:
function Box(){}; Box.prototype={ Constructor:Box, name:"link", age:25, family:['哥哥','弟弟','妹妹'], run:function(){ return this.name+this.age+'运行中。。。' } } var box1=new Box(); alert(box1.family) box1.family.push('姐姐') //添加一个引用类型 alert(box1.family) // 输出 哥哥弟弟妹妹姐姐 var box2=new Box(); alert(box2.family) //共享了box1添加的引用类型的原型 // 输出 哥哥弟弟妹妹姐姐
ps:数据共享的缘故,导致很多开发者放弃使用原型,因为每次实例化出的数据需要保留自己的特性,而不能共享。
为了解决构造传参和共享问题,可以组合构造函数+原型模式
function Box(name,age){ //不共享的使用构造函数 this.name=name; this.age=age; this.family=['哥哥','弟弟','妹妹']; } Box.prototype={ //共享的使用原型模式 constructor:Box, run:function(){ return this.name+this.age+'运行中。。。' } } var box1=new Box('link',25); box1.family.push('姐姐') alert(box1.run()) alert(box1.family) // 输出 哥哥弟弟妹妹姐姐 var box2=new Box('links',25); alert(box2.run()) alert(box2.family) // 输出 哥哥弟弟妹妹
ps:这种混合模式很好的解决了传参和引用共享的大难题。是创建对象比较好的方法。
原型模式,不管你是否调用了原型中的共享方法,它都会初始化原型中的方法,并且在声明一个对象时,构造函数+原型部分很让人怪异,最好就是把构造函数和原型封装到一起。为了解决这个问题,我们可以使用动态原型模式。
//可以将原型封装到构造函数里面 function Box(name,age){ this.name=name; this.age=age; alert('初始化开始') Box.prototype.run=function(){ return this.name+this.age+'运行中...' } alert('初始化结束') } //原型初始化,执行一次就行,没有必要每次都构造函数实例化的时候初始化! var box1=new Box('link',53); alert(box1.run()) var box2=new Box('that',5311); alert(box2.run())
原型初始化,执行一次就行,没有必要每次都构造函数实例化的时候初始化!
function Box(name,age){ this.name=name; this.age=age; if(typeof this.run!='function'){ //查看this是否有run alert('初始化开始') Box.prototype.run=function(){ return this.name+this.age+'运行中...' } alert('初始化结束') } } //原型初始化,执行一次就行! var box1=new Box('link',53); alert(box1.run()) var box2=new Box('that',5311); alert(box2.run())
ps:使用动态原型模式,要注意一下,就是不可以再使用字面量的方式重写原型,因为会切断实例和新原型之间的关联。
如果这几种方式不能满足需求,可以使用一开始那种模式:寄生构造函数!
function Box(name,age){ var obj = new Object(); obj.name=name; obj.age=age; obj.run=function(){ return this.name+this.age+"运行中....." } return obj } var box1=new Box('link',53); alert(box1.run())
ps:寄生构造函数,其实就是工厂模式+构造函数模式。这种模式比较通用,但是不能确定对象关系,所有,在可以使用之前所说的模式时,不建议使用此模式!
在什么情况下使用寄生构造函数比较合适呢?
假如要创建一个额外方法的引用类型。由于之前说明不建议直接使用String.porotype.addstring方法,
可以使用寄生构造的方式添加:
function mystring(string){ var str=new String(string); str.addString=function(){ return this+',添加'; } return str; } var box1=new mystring('link'); alert(box1.addString());
在一些安全的环境中,比如禁止使用this和new,这里的this是构造函数里不使用this,这里的new是在外部实例化构造函数时不使用new,
这种创建方式叫稳妥构造函数。
function Box(name,age){ var obj = new Object(); obj.run=function(){ return name+age+"运行中....." } return obj } var box1=Box('link',53); alert(box1.run())
ps:稳妥构造函数和寄生类似。