1.工厂模式javascript
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("Greg",27,"Doctor");
优势:解决了建立多个类似对象的问题。java
缺点:没有解决对象识别问题(即怎样知道一个对象的类型)安全
2.构造函数模式函数
function Person(name,age,job){ this.name=name; othis.age=age; this.job=job; this.sayName=function(){ alert(this.name); }; } var person1=new Person("Nicholas",29,"Software Engineer"); var person2=new Person("Greg",27,"Doctor");
与工厂模式相比,这里没有显式地建立对象;直接将属性和方法赋给了this对象;没有return语句。按照惯例,构造函数始终都应该以一个大写字母开头,而非构造函数应该以一个小写字母开头。在这个例子中,person1和person2分别保存着Person的一个不一样实例,这两个对象都有一个constructor(构造函数)属性。任何函数,只要经过new操做来调用,那它就能够做为构造函数;而任何函数,若是不经过new操做符来调用,那它和普通函数就没什么两样。this
优势:建立自定义的构造函数意味着未来能够将它的实例标识为一种特定的类型;而这正是构造函数模式赛过工厂模式的地方。spa
缺点:每一个方法都要在每一个实例上从新建立一遍。prototype
改进:指针
function Person(name,age,job){ this.name=name; this.age=age; this.job=job; this.sayName=sayName; } function sayName(){ alert(this.name); }; var person1=new Person("Nicholas",29,"Software Engineer"); var person2=new Person("Greg",27,"Doctor");
将sayName()函数的定义转移到了构造函数外部。而构造函数内部,咱们将sayName属性设置成等于全局的sayName函数。这样一来,因为sayName包含的是一个指向函数的指针,所以person1和person2对象就共享了在全局做用域中定义的同一个sayName()函数。但是若是对象须要定义不少方法,那么就要定义不少全局函数,因而咱们这个自定义的引用类型就丝毫没有封装性可言。code
3.原型模式对象
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(); var person2=new Person(); person2.sayName();
alert(person1.sayName==person2.sayName); //true
咱们建立的每个函数都有一个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(); var person2=new Person(); person1.name="Greg"; alert(person1.name);//"Greg"——来自实例 alert(person2.name);//"Nicholas"——来自原型
使用hasOwnProperty()方法能够检测一个属性是存在于实例中,仍是存在于原型中。它是从object继承来的,只在给定属性存在于对象实例中时,才会返回TRUE。上面例子中person1重写了name属性,故调用person1.hasOwnProperty("name");时才会返回true;
你们应该注意到了,前面例子中每添加一个属性和方法就要敲一遍Person.prototype,为减小没必要要的输入,也为了从视觉上更好地封装原型的功能,更常见的方法是用一个包含全部属性和方法的对象字面量来重写整个原型对象。
function Person(){ } Person.prototype={ name:"Nicholas", age:29, job:"software engineer", sayName:function(){ alert(this.name); } };
可是上面的方法会使constructor属性再也不指向Person了,为此能够在Person.prototype添加一句,constructor:Person,以确保经过该属性能够访问到适当的值。
优势:可让全部对象实例共享它所包含的实例和方法。换句话说,没必要在构造函数中定义对象实例的信息,而是能够将这些信息直接添加到原型对象中。
缺点:它省略了为构造函数传递初始化参数这一环节,结果全部实例在默认状况下都将取得相同的属性值。这种共享对于函数很是合适,对于那些包含基本值的属性也说得过去,但对于包含引用类型值的属性来讲,问题就比较突出了。以下:
function Person(){ } Person.prototype={ constructor:Person, name:"Nicholas", age:29, job:"Software Engineer", friend:["one","two"], sayName:function(){ alert(this.name); } }; var person1=new Person(); var person2=new Person(); person1.friend.push("three"); alert(person1.friend); //"one,two,three" alert(person2.friend); //"one,two,three" alert(person1.friend===person2.friend);//true
这也是不多看到有人单独使用原型模式的缘由所在。
4.组合使用构造函数模式和原型模式
function Person(name,age,job){ this.name=name; this.age=age; this.job=job; this.friends=["one","two"]; } Person.prototype={ constructor:Person, sayName:function(){ alert(this.name); } } var person1=new Person("Nicholas",29,"softwarre Engineer"); var person2=new Person("Greg",27,"Doctor"); person1.friends.push("three"); alert(person1.friends); //"one,two,three" alert(person2.friends); //"one,two" alert(person1.friends==person2.friends); //false alert(person1.sayName==person2.sayName); //true
这种组合模式中构造函数模式用于定义实例属性,而原型模式用于定义方法和共享的属性。这种组合模式是在ECMAScript中使用最普遍,认同度最高的一种建立自定义类型的方法。
5.动态原型模式
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();
这里只在sayName()方法不存在的状况下才会将它添加到原型中。这段代码只有在初次调用的时候才会执行。
6.寄生构造函数模式
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();
一般在前面几种模式都不适用的状况下,可使用寄生构造函数模式。这种模式的基本思想是建立一个函数,该函数的做用仅仅是封装建立对象的代码,而后再返回新建立的对象。关于寄生构造函数模式,有一点要说明:首先,返回的对象与构造函数或者与构造函数的原型属性之间没有关系;也就是说,构造函数的返回的对象与在构造函数外部建立的对象没有什么不一样。为此不能依赖instanceof操做符来肯定对象类型。
7.稳妥构造函数模式
function Person(name,age,job){ var o=new Object(); //能够在这里定义私有变量和函数 o.sayName=function(){ alert(name); }; return o; } var friend=Person("Nicholas",29,"Software Engineer"); friend.sayName();
Douglas Crockford发明了javascript中的稳妥对象这个概念。所谓稳妥对象,指的是没有公共属性,并且其方法也不引用this的对象。稳妥对象最适合在一些安全的环境中(这些环境会禁止使用this和new)时使用。稳妥构造函数遵循与寄生构造函数相似的模式,但有两点不一样:一是新建立对象的实例不引用this;二是不使用new操做符调用构造函数。在上面的例子中,除了sayName()方法外,没有别的方式能够访问其数据成员。