虽然Object构造函数或者对象字面量都可以用来创建单个对象,但是缺点非常明显:使用同一接口创建很多对象,会产生大量重复代码。
工厂模式
1 function CreatePerson(name,age,job){
2 var o=new Object();
3 o.name=name;
4 o.age=age;
5 o.job=job;
6 o.sayName=function(){
7 alert(this.name);
8 }
9 return o;
10 }
11 var person1=CreatePerson("Jim",29,"Teaching in Colleage");
12 var person2=CreatePerson("Merry",23,"Study in Colleage");
13 console.log(person1.sayName==person2.sayName);
解决了创建多个对象的问题,但是却没有解决对象识别的问题。
构造函数模式
1 // 构造函数模式
2 /*
3 必须使用 new 操作符,会经过以下几个步骤
4 1 创建一个新对象
5 2 将构造函数的作用域赋值给新对象
6 3 执行构造函数中的代码(为新对象添加属性)
7 4 返回新对象
8 */
9 function PersonByConstructor(name,age,job){//按照惯例构造函数以大写字母开头
10 this.name=name;
11 this.age=age;
12 this.job=job;
13 this.sayName=function(){
14 alert(this.name);
15 };
16 }
17 var person3=new PersonByConstructor("John",24,"Software Engineer");
18 var person4=new PersonByConstructor("Greg",25,"Doctor");
19
20 console.log(person3 instanceof PersonByConstructor);//返回true
21 console.log(person4 instanceof PersonByConstructor);
创建自定义的构造函数意味着将来可以将它的实例标识为一种特定的类型。
任何函数,只要通过new 操作符来调用,那它就可以作为构造函数;否则和普通函数一样。
1 PersonByConstructor("Lily",32,"Driver");//添加到window
2 window.sayName();
3
4 var o=new Object();
5 PersonByConstructor.call(o,"Kristen",23,"Nurse");
6 o.sayName();
构造函数模式虽然好用,但是每个方法都要在每个实例上重新创建一遍。
可以将方法移到构造函数的外部解决,但是如对象要调用很多方法,则需要定义很多全局函数,这样就没有封装性可言了。这个问题可以通过原型模式来解决。
原型模式
我们创建的每一个函数都有原型属性(prototype),这个属性是一个指向对象的指针,这个对象的用途是包含可以由特定类型的所有实例共享的属性和方法。prototype就是通过调用构造函数而创建的那个对象实例的原型对象。使用对象原型的好处是让所有对象实例共享它所包含的属性和方法。
理解原型对象
无论什么时候,只要创建一个对象,都会根据一定的规则为该函数创建一个prototype属性,这个属性指向函数的原型对象。在默认情况下所有原型都会自动获取一个constructor(构造函数)属性,这个属性包含一个指向prototype属性所在函数的指针。
function PersonByPrototype(){
}
PersonByPrototype.prototype={
name:"Jim",
age:21,
job:"Teaching",
sayName:function(){
console.log(this.name);
}
}
var person5=new PersonByPrototype();
person5.sayName();
var person6= new PersonByPrototype("Mick",32,"assitant");
person6.sayName();
console.log(PersonByPrototype.prototype.isPrototypeOf(person5));//通过 prototype.isPrototypeOf 来确定PersonByPrototype是否是person5的原型
console.log(Object.getPrototypeOf(person5).name);//Object.getPrototypeOf可以方便的获取对象的原型,这在利用原型实现继承的情况下是非常重要的
每当代码读取某个对象的属性时,都会指向一次搜索,目标是具有给定名字的属性。搜索首先从对象实例本身开始,如果找到则返回该属性的值,如果没找到,则搜索指针指向原型对象,在原型对象查找具有给定名字的属性。我们对原型的修改会立刻的体现出来。
当为对象实例添加新的属性时,这个属性就会屏蔽 原型对象 中的那个同名属性,换句话说,会阻止我们访问原型中的属性,但不会修改那个属性。不过使用delete可以删除实例中的属性,从而使我们能够访问原型中的属性。
console.log(person6.hasOwnProperty("name")); //hasOwnProperty 来判断属性是实例的还是原型继承来的
console.log(person5.hasOwnProperty("name"));
原型与 IN 操作符
在单独使用时,in 操作符会在通过对象能够访问给定属性时返回 true,无论该属性存在于实例中还是原型中。只要 in 操作符返回 true, 而 hasOwnProperty返回false,就可以确定属性是原型中的属性。
function hasPrototypeProperty(name,obj){
return !obj.hasOwnProperty("name") && ("name" in obj);
}
在使用 for-in 循环时,返回的是通过对象访问的、可枚举的属性,其中包括实例和原型对象中的属性。
for (var prop in person5)
console.log(prop); //返回 name,age ,job, sayName
原型的动态性
实例中的指针仅指向原型,而不是构造函数。
如果把原型改为另外一个对象,相当于切断了构造函数和最初原先对象之间的联系。
原生对象的问题
1 省略了为构造函数传参这一环节
2 原型中所有属性都是被所有实例共享的。
组合使用原型模式和构造函数模式
构造函数模式用于定义实例属性,原型模式来定义方法和共享的属性。
function PersonByConstructorAndPrototype(name,age,job){ this.name=name; this.age=age; this.job=job; this.friends=[]; } PersonByConstructorAndPrototype.prototype={ constructor:PersonByConstructorAndPrototype, sayName:function(){console.log(this.name);} } var person7=new PersonByConstructorAndPrototype("LiLei",12,"Student"); var person8 = new PersonByConstructorAndPrototype("LiuLei",13,"Student") person7.friends.push("LiuLei"); person8.friends.push("LiLei"); console.log(person7.friends.toString()); console.log(person8.friends);
动态原型模式
有其他 OO 语言经验的开发人员在看到独立的构造函数和原型时,很可能会感到非常困惑。动态原型模式正是致力于解决这个问题的一个方案,它把所有信息都封装在了构造函数中,而通过在构造函数中初始化原型(仅在必要的情况下),又保持了同时使用构造函数和原型的优点。换句话说,可以通过检查某个应该存在的方法是否有效,来决定是否需要初始化原型。
寄生构造函数模式
如果上面几种方式都不适用,可以适用寄生模式。该模式的核心思想是创建一个函数,该函数的作用仅仅是封装创建对象的代码。
//寄生构造函数模式 function PersonByParasitic(name,age,job){ var o=new Object(); o.name=name; o.age=age; o.job=job; o.sayName=function(){ console.log(this.name); }; return o; } var friend=new PersonByParasitic("Nicholas",20,"Software Engineer"); friend.sayName(); var friend2=new PersonByParasitic("Nick",32,""); friend2.sayName();
这个模式可以在特殊情况下为对象创建构造函数。
稳妥构造函数模式
稳妥对象指的是没有公共属性,而其方法也不引用this对象。