综上所述,组合使用构造函数模式和原型模式可以说是非常完美了。
第六部分:动态原型模式、寄生构造函数模式、稳妥构造函数模式实际上,组合使用构造函数模式和原型模式确实已经非常完美了,这里将要讲的几种模式都是在特定的情况下使用的,所以我认为第六部分相对于第五部分并没有进一步的提高。仅仅是多学习几种模式可以解决更多的问题。
A 动态原型模式这里的动态原型模式相对于第五部分的组合使用自定义构造函数模式和原型模式本质上是没有什么差别的,只是因为对于有其他OO(Object Oriented,面向对象)语言经验的开发人员看到这种模式会觉得奇怪,因此我们可以将所有信息都封装在构造函数中。本质上是通过检测某个应该存在的方法是否存在或有效,来决定是否要初始化原型。如下例所示:
1 2 3 4 5 6 7 8 9 10 11 12 13
function Person(name,age,school){ this.name=name; this.age=age; this.school=school; if(typeof this.sayName != "function"){ Person.prototype.sayName=function(){ console.log(this.name); }; } } var person=new Person("zzw",21,"xjtu");//使用new调用构造函数并创建一个实例对象 person.sayName(); //zzw console.log(person.school);//xjtu
这里先声明了一个构造函数,然后当使用new操作符调用构造函数创建实例对象时进入了构造函数的函数执行环境,开始检测对象的sayName是否存在或是否是一个函数,如果不是,就使用原型修改的方式向原型中添加sayName函数。且由于原型的动态性,这里所做的修改可以在所有实例中立即得到反映。值得注意的是,在使用动态原型模式时,不能使用对象字面量重写原型,否则,在建立了实例的情况下重写原型会导致切断实例和新原型的联系。
B 寄生构造函数模式寄生构造函数模式是在前面几种模式都不适用的情况下使用的。看以下例子,再做出说明:
1 2 3 4 5 6 7 8 9 10 11 12
function Person(name,age,school){ var o =new Object(); o.name=name; o.age=age; o.school=school; o.sayName=function(){ console.log(this.name); }; return o; } var person = new Person("zzw",21,"xjtu"); person.sayName();//zzw
寄生构造函数的特点如下:
声明一个构造函数,在构造函数内部创建对象,最后返回该对象,因此这个函数的作用仅仅是封装创建对象的代码。
可以看出,这种方式除了在创建对象的时候使用了构造函数的模式(函数名大写,用new关键字调用)以外与工厂模式一模一样。
构造函数在不返回值的情况下,默认会返回新对象实例,而通过构造函数的末尾添加一个return语句,可以重写调用构造函数时返回的值。
这个模式可以在特殊的情况下来为对象创建构造函数。假设我们想要创建一个具有额外方法的特殊数组,通过改变Array构造函数的原型对象是可以实现的,但是我在第四部分F中提到过,这种方式可能会导致后续的命名冲突等一系列问题,我们是不推荐的。而寄生构造函数就能很好的解决这一问题。如下所示:
1 2 3 4 5 6 7 8 9 10
function SpecialArray(){ var values=new Array(); values.push.apply(values,arguments); values.toPipedString=function(){ return this.join("|"); }; return values; } var colors=new SpecialArray("red","blue","green"); console.log(colors.toPipedString());//red|blue|green
或者如下所示:
1 2 3 4 5 6 7 8 9 10
function SpecialArray(string1,string2,string3){ var values=new Array(); values.push.call(values,string1,string2,string3); values.toPipedString=function(){ return this.join("|"); }; return values; } var colors=new SpecialArray("red","blue","green"); console.log(colors.toPipedString());//red|blue|green
这两个例子实际上是一样的,唯一差别在于call()方法和apply()方法的应用不同。(这部分内容详见《JavaScript函数之美~》)
这样就既没有改变Array构造函数的原型对象,又完成了添加Array方法的目的。