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("mike",32,"teacher");
var person2 = createPerson("jim",33,"Doctor");
说明:工厂模式解决了创建多个相似对象的问题,但却没有解决对象识别的问题(即怎样知道一个对象的类型)。
二、构造函数模式,实例代码:
function Person(name,age,job){
this.name=name;
this.age=age;
this.job=job;
this.sayName= function () {
alert("this.name");
};
}
var person1 = new Person("mike",32,"Teacher");
var person2 = new Person("jim",33,"Doctor");
alert(person1 instanceof Object); //true
alert(person1 instanceof Person); //true
alert(person2 instanceof Object); //true
alert(person2 instanceof Person1); //true
说明:
1、构造函数与其他函数的唯一区别,就在于调用它们的方式不同,任何函数,只要通过new操作符来调用,那它就可以作为构造函数,如果不使用new来调用,那它就是普通的函数。实例代码:
//当构造函数使用
var person = new Person("james",29,"Coder");
person.sayName(); //"james"
//当普通函数使用
Person("crise",29,"Doctor"); //添加到window
window.sayName(); //"crise"
//在另一个对象的作用域中调用
var o = new Object();
Person.call(0,"kristen",23,"Nurse");
o.sayName(); //kristen
2、我们发现,两个person的sayName()方法是一样的,但是却实例化了两次。。。可以将相同的方法放到原型对象中,就是下面的方法:
三、原型模式
每个函数都有一个prototype属性,这个属性是一个指针,指向函数的原型对象,这个对象一般放实例共享的属性和方法。实例代码:
function Person(){
}
Person.prototype.name = "Nicholas";
Person.prototype.age = 29;
Person.prototype.job = "Software Engineer";
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
alert(Person.prototype.isPrototypeOf(person1)); //true
alert(Person.prototype.isPrototypeOf(person2)); //true
//only works if Object.getPrototypeOf() is available
if (Object.getPrototypeOf){
alert(Object.getPrototypeOf(person1) == Person.prototype); //true
alert(Object.getPrototypeOf(person1).name); //"Nicholas"
}
说明:
1、原型中的属性和方法是实例共享的,实例的原型对象和构造函数的prototype指向的对象是一个对象。关系图如下:
2、实例对象可以访问原型对象中的值,但是却不能通过实例对象重写原型中的值,只能覆盖(或者说是屏蔽),当然可以使用delete删除实例对象中的与原型对象中同名的属性。
3、同时使用hasOwnPreperty()方法和in操作符,来确定某个属性到底是存在于对象中,还是原型中,代码:
function hasPrototypeProperty(object,name){
return !object.hasOwnProperty(name) && (name in object):
}
4、可以使用Object.keys()方法,取得对象上所有可枚举的实例属性,参数是对象,返回值是一个包含所有可枚举属性的字符串数组。例如:
function Person(){
}
Person.prototype.name="Nicolas";
Person.prototype.age=19;
Person.prototype.job="Soft Engineer";
Person.prototype.sayName=function(){
alert(this.name);
};
var keys=Object.keys(Person.prototype);
alert(keys); //"name,age,job,sayName"
var p1 = new Person();
p1.name="Rob";
p1.age=31;
var p1keys=Object.keys(p1);
alert(p1keys); //"name,age"
5、可以使用Object.getOwnPropertyNames()方法得到所有实例属性(包括原型中的),无论是否可枚举,实例代码:
var keys=Object.getOwnPropertyNames(Person.prototype);
alert(keys); //"constructor,name,age,job,sayName"
6、可以将一个对象赋给构造函数的原型对象,实例代码:
function Person (){
}
Person.prototype={
constructor:Person,
name:"Nicholas",
age:23,
job:"soft Engineer",
sayName:function(){
alert(this.name);
}
};
注意,此时constructor属性会变成“可枚举”了,但是默认情况下使不可枚举的,咱们这样改一下:
function Person (){
}
Person.prototype={
name:"Nicholas",
age:23,
job:"soft Engineer",
sayName:function(){
alert(this.name);
}
};
//重设构造函数,只适用于ECMAscript5兼容的浏览器
Object.defineProperty(Person.prototype."constructor",{
enumerabel:false,
value:Person
});
注意:这种重写整个原型对象的做法,会切断现有原型与任何之前已经存在的对象实例之间的联系,就是说,先声明对象实例,后重写原型对象,则之前声明的对象实例的原型还是没有修该的那个,是不会跟着变的,但是在重写原型对象之后声明的对象实例的原型对象就跟着变成的重写之后的对象(这是JavaScript对象继承的理论基础),实例代码:
function Person (){
}
Person.prototype.age=44;
var person1 = new Person();
Person.prototype={
constructor:Person,
name:"Nicholas",
age:23,
job:"soft Engineer",
sayName:function(){
alert(this.name);
}
};
person2 = new Person();
person1.age; //44
person2.age; //23
示例图:
7、这个模式,有不足之处,就是所有实例对象都会共享原型对象中的属性和方法,但是,我是我们希望各个实例对象有自己独立的属性和方法。可以组合使用构造函数模式和原型模式,只将需要共享的属性和方法放入原型对象中,而将各自独立的属性和方法放到构造函数中即可。如下节,
四、组合使用构造函数模式和原型模式
实例代码:
function Person(name,age,job){
this.name=name;
this.age=age;
this,job=job;
this.friends=[];
}
Person.prototype={
constructor:Person,
sayName:function(){
alert(this.name);
}
}
说明:
这种构造函数与原型混合的模式,是目前在ECMAScript中使用最广泛、认同度最高的一种创建自定义类型的方法。可以说是一种默认模式!
五、动态原型模式
可以通过检查某个应该存在的方法是否有效,来决定是否需要初始化原型,实例代码:
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);
}
}
}
注意,if语句只会在初次调用构造函数时才会执行。
六、寄生构造函数
基本思想是创建一个函数,该函数的作用仅仅是封装创建对象的代码,然后再返回对象创建的对象,从表面上看,这个函数又很像是典型的构造函数,实例代码:
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",23,"soft Engineer");
friend.sayName(); //"Nicholas"
说明:
1、返回的对象与构造函数的原型属性之间没有关系,所有不能依赖instanceof操作符来确定对象的类型,所以建议在可以使用其他模式的情况下,不要使用这个模式。
七、稳妥构造函数模式
稳妥对象是指没有公共属性,而且其方法不引用this的对象,稳妥对象最适合在一些安全的环境中(这些环境中会禁止使用this和new),或者在防止数据被其他应用程序改动时使用。稳妥构造函数模式与寄生构造函数模式类似,区别:新创建对象的实例方法不引用this而且不使用new操作符调用构造函数,示例代码:
function Person(name,age,job){
var o = new Object();
o.name=name;
o.age=age;
o.job=job;
o.sayName=function(){
alert(name);
};
return o;
}
var friend =Person("Nicholas",23,"soft Engineer");
friend.sayName(); //"Nicholas"
这样,变量person中保持的是一个稳妥对象,而除了调用sayName()方法外,没有别的方法可以访问其数据对象。