JavaScript高级程序设计学习笔记--面向对象程序设计
工厂模式
虽然Object构造函数或对象字面量都可以用来创建单个对象,但这些方式有个明显的缺点:使用同一个接口创建很多对象,会产生大量的重复代码。为解决这个问题,人们开始使用
工厂模式的一种变体。
function createPerson(name,age,job){ var o=new Object(); o.name=name; o.age=age; o.job=job; o.sayName=function(){ alert(this.name); }; return o; } var person1=createPerson("Nicholas",29,"Software Engineer"); var person2=createPerson("Grey",27,"Doctor");
函数createPerson()能够根据接受的参数来构建一个包含所有必要信息的Person对象。可以无数次的调用这个函数,而每次它都会返回一个包含三个属性一个方法的对象。
工厂模式虽然解决了创建多个相似对象的问题,但却没有解决对象识别的问题(即怎样知道一个对象的类型)。
构造函数模式
function Person(name,age,job){ this.name=name; this.age=age; this.job=job; this.sayName=function(){ alert(this.name); } } var person1=new Person("Nicholas",29,"Software Engineer"); var person2=new Person("Grey",27,"Doctor");
在这个例子中,Person()函数取代了createPerson函数。我们注意到,Person()中的代码除了与createPerson()中相同的部分外,还存在以下不同之处:
(1):没有显式的创建对象;
(2):直接将属性和方法赋给了this对象;
(3):没有return语句
原型模式
我们创建的每个函数都有一个prototype(原型)属性,这个属性是一个指针,指向一个对象,而这个对象的用途是包含可以由特定类型的所有实例共享的属性和方法。
function Person(){ } Person.prototype.name="Nicholas"; Person.prototype.age=29; Person.prototype.sayName=function(){ alert(this.name); } var person1=new Person(); person1.sayName(); //"Nicholas" var person2=new Person(); person2.sayName(); //"Nicholas" alert(person1.sayName==person2.sayName); //true
虽然可以通过对象实例访问保存在原型中的值,但却不能通过对象实例重写原型中的值。如果我们在实例中添加一个属性,而该属性与实例原型中的一个属性同名,那我们就在实例
中创建该属性,该属性将会屏蔽原型中的属性。
原型的动态性
由于在原型中查找值的过程是一次搜索,因此我们对原型对象所做的任何个修改都能够立即从实例上反映出来--即使先创建了实例后个性原型也照样如此。请看下面的例子:
var friend=new Person(); Person.prototype.sayHi=function(){ alert("HI"); } friend.sayHi(); //"HI" (没有问题!)
以上代码先创建了Person的一个实例,并将其保存在person中,然后,下一条语句在Person.prototype中添加了一个方法sayHi().即使person实例是在添加新方法之前创建的,
但它仍然可以访问这个新方法。
尽管可以随时为原型添加属性和方法,并且个性能够立即在所有对象实例中反映出来,但如果是重写整个原型对象,那么情况就不一样了。我们知道,调用构造函数时会为实例
添加一个指向最初原型的[[Prototype]]指针,而把原型修改为另外一个对象就等于切断了构造函数与最初原型之间的联系。请记住:实例中的指针仅指向原型,而不指向构造
函数。看下面的例子:
function Person(){ } var friend=new Person(); Person.prototype={ constructor:Person, name:"Nicholas", age:29, job:"Software Engineer", sayName:function(){ alert(this.name); } }; friend.sayName(); //error
动态原型模式
可以通过检查某个应该存在的方法是否有效,来决定是否需要初始化原型。来看下面一个例子:
function Person(name,age,job){ this.name=name; this.age=age; this.job=job; if(typeof this.sayName!="function"){ Person.prototype.sayName=function(){ alert(this.name); } } } var friend=new Person("Nicholas",29,"Software Engineer"); friend.sayName();
寄生构造函数模式
通常,在前述几种模式都不适用的情况下,可以使用寄生(parasitic)构造函数模式。这种模式的基本思想是创建一个函数,该函数的作用仅仅是封装创建对象的代码,然后再返回
新创建的对象;但从表面上看,这个函数又很像是典型的构造函数。下面是一个例子:
function Person(name,age,job)( var o=new Object(); o.name=name; o.age=age; o.job=job; o.sayName=function(){ alert(this.name); } return o; } var friend=new Person("Nicholas",29,"Software Engineer"); friend.sayName(); //"Nicholas"
在这个例子中,通过在构造函数的末尾添加一个return语句,可以重写调用构造函数时返回的值。
这个模式可以在特殊情况下用来为对象创建构造函数。假设我们想创建一个具有额外方法的特殊数组。由于不能直接修改Array构造函数,因此可以使用这个模式。
function SpeicalArray(){ var values=new Array(); //创建数组 values.push.apply(values,arguments); //添加值 values.toPipedString=function(){ return this.join("|"); } return values; //返回数组 } var colors=new SpeicalArray("red","blue","green"); alert(colors.toPipedString()); //"red|blue|green"