创建对象:虽然Object构造函数或对象自面了都可以用来创建单个对象,但这些方式有个明显的缺点:使用同一个接口创建多个对象,会产生大量的重复代码。所以为了解决这个问题,如下:
1.工厂模式
抽象了具体创建对象的过程,即用函数来封装以特定接口创建对象的细节。
eg:
<span style="font-size:10px;"><span style="font-size:10px;"><span style="white-space:pre"> </span>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("Alin", 22, "web");
var person2 = createPerson("Leeon", 21, "java web");</span></span>
可以无数次的调用这个函数,而每次它都会返回一个包含三个属性的一个方法的对象。工厂模式虽然解决了创建多个相似对象的问题,但却没有解决对象识别的问题(即怎样知道一个对象的类型)。
2.构造函数模式
像Object和Array这样的原生构造函数,在运行时会自动出现在执行环境中。也可以创建自定义的构造函数,从而定义自定义对象类型的属性和方法。例如,也可以使用构造函数模式将前面的例子重写如下:
eg:
<span style="font-size:10px;"><span style="font-size:10px;"><span style="white-space:pre"> </span>function Person(name, age, job){
this.name = name;
this.age = age;
this.job = job;
this.sayName = function(){
alert(this.name);
};
}
var person1 = new Person("Alin", 22, "web");
var person2 = new Person("Leeon", 21, "java web");</span></span>
这个例子中Person()取代了createPerson(),存在以下不同:
a.没有显示的创建对象;
b.直接将属性和方法赋值给了this对象;
c.没有return语句。
注:按照惯例,构造函数始终都应该以一个大写字母开头。构造函数本事也是函数,只不过可以用来创建对象而已。
这个例子的最后,person1和person2分别保存着Person的一个不同实例。这两个对象都有 一个constructor(构造函数)属性,该属性指向Person。如下:
alert(person1.constructor == Person);//true
alert(person2.constructor == Person);//true
instanceof操作符检测对象的实例更靠谱一些。这个例子中我们创建的所有对象既是Object的实例,又是person的实例。例如:
alert(person1 instanceof Object);//true
alert(person1 instanceof Person);//true
创建自定义构造函数意味着将来可以将它的实例标识为一种特定的类型,这正是构造函数模式胜过工厂模式的地方。
1>将构造函数当做函数
任何函数,只要通过new操作符来调用,那它就可以作为构造函数;而任何函数,如果不通过new 操作符来调用,那它跟普通函数也不会有什么两样。
构造函数的典型用法:
a> //当构造函数使用
var person = new Person("Alin", 22, "web");
person.sayName();//Alin
b> //作为普通函数调用
Person("Leeon", 21, "java web");
window.sayName();//Leeon 属性和方法都被添加给了window对象。
注:当在全局作用域中调用一个函数时,this对象总是指向Global对象即就是浏览器中的window对象。
c>//在另一个对象的作用域中调用
var o = new Object();
Person.call(o, "Alin", 25, "java web");
o.sayName();//Alin
2>构造函数的问题
缺点:每个方法都要在每个实例上重新创建一遍。每定义一个函数也就是实例化了一个对象。
从逻辑角度讲,构造函数也可以这样定义。
eg:
<span style="font-size:10px;"><span style="font-size:10px;"><span style="white-space:pre"> </span>function Person(name, age, job){
this.name = name;
this.age = age;
this.job = job;
this.sayName = new Function("alert(this.name)");//与声明函数在逻辑上是等价的
};
}</span></span>
以这种方式创建函数,会导致不同的作用域链和标识符解析,但创建Function新实例的机制仍然是相同的。因此不同实例上的同名函数是等价的。一下代码得以证明:
alert(person1.sayName == person2.sayName);//false
可以像下面这样把函数定义在构造函数外部,因为创建两个完成相同任务的Function实例没有必要,况且this对象在,不需要在执行代码前就把函数绑定在特定对象上。
eg:
<span style="font-size:10px;"><span style="font-size:10px;"><span style="white-space:pre"> </span>function Person(name, age, job){
this.name = name;
this.age = age;
this.job = job;
this.sayName = sayName;//全局的sayName()函数,sayName包含了一个指向函数的指针,因此两个函数就共享了全<span style="white-space:pre"> </span> //局作用域中定义的同一个sayName()函数。
}
function sayName(){
alert(this.name);
}
var person1 = new Person("lilinwei", 22, "web");
var person2 = new Person("zhangziliang", 21, "java web");</span></span>
缺点:在全局作用域中定义的函数实际上是只能被某个对象所调用,让全局作用域有点名不副实。而且如果需要定义很多方法,就需要定义多个全局函数,没有丝毫的封装性。
所以原型模式来解决。
3.原型模式
创建的每个函数都有一个prototype(原型)属性,这个属性是一个指针,指向一个对象,这个对象包含特定类型的所有实例共享的属性和方法。使用原型的好处:可以让所有实例共享它所包含的属性和方法。
eg:
<span style="font-size:10px;">function Person(){
}
Person.prototype.name = "Alin";
Person.prototype.age = 22;
Person.prototype.job = "web";
Person.prototype.sayName = function(){
alert(this.name);
}
var person1 = new Person();
person1.sayName(); //Alin
var person2 = new Person();
person2.sayName(); //Alin
alert(person1.sayName == person2.sayName); //true</span>