JavaScript建立对象的7种模式总结

一、工厂模式

 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 
12 var person1 = createPerson("Nicholas", 29, "Software Engineer");
13 var person2 = createPerson("Greg", 27, "Doctor");

函数createPerson()可以根据接受的参数来构建一个包含全部必要信息的Person对象。能够无数次地调用这个函数,而每次它都会返回一个包含三个属性一个方法的对象。工厂模式虽然解决了建立多个类似对象的问题,但却没有解决对象识别的问题(即怎样知道一个对象的类型)。浏览器

二、构造函数模式

 1 function Person(name, age, job){
 2     this.name = name;
 3     this.age = age;
 4     this.job = job;
 5     this.sayName = function(){
 6         alert(this.name);
 7     };    
 8 }
 9 
10 var person1 = new Person("Nicholas", 29, "Software Engineer");
11 var person2 = new Person("Greg", 27, "Doctor");
12 
13 person1.sayName();   //"Nicholas"
14 person2.sayName();   //"Greg"
15 
16 alert(person1 instanceof Object);  //true
17 alert(person1 instanceof Person);  //true
18 alert(person2 instanceof Object);  //true
19 alert(person2 instanceof Person);  //true
20 
21 alert(person1.constructor == Person);  //true
22 alert(person2.constructor == Person);  //true
23 
24 alert(person1.sayName == person2.sayName);  //false        

与工厂模式的不一样之处:函数

  • 没有显式地建立对象;
  • 直接将属性和方法赋给了this对象;
  • 没有return语句。

缺点:this

使用构造函数,每一个方法都要在每一个实例上从新建立一遍,不一样实例上的同名函数是不相等的,致使内存浪费。spa

三、原型模式

咱们建立的每一个函数都有一个prototype(原型)属性,这个属性是一个指针,指向一个对象,而这个对象的用途是包含能够由特定类型的全部实例共享的属性和方法。prototype

 1 function Person(){
 2 }
 3 
 4 Person.prototype.name = "Nicholas";
 5 Person.prototype.age = 29;
 6 Person.prototype.job = "Software Engineer";
 7 Person.prototype.sayName = function(){
 8     alert(this.name);
 9 };
10 
11 var person1 = new Person();
12 person1.sayName();   //"Nicholas"
13 
14 var person2 = new Person();
15 person2.sayName();   //"Nicholas"
16 
17 alert(person1.sayName == person2.sayName);  //true
18 
19 alert(Person.prototype.isPrototypeOf(person1));  //true
20 alert(Person.prototype.isPrototypeOf(person2));  //true
21 
22 //only works if Object.getPrototypeOf() is available
23 if (Object.getPrototypeOf){
24     alert(Object.getPrototypeOf(person1) == Person.prototype);  //true
25     alert(Object.getPrototypeOf(person1).name);  //"Nicholas"
26 }

新对象的这些属性和方法是由全部实例共享的,因此person1和person2访问的都是同一组属性和同一个sayName()函数。指针

虽然能够经过对象实例访问保存在原型中的值,但却不能经过对象实例重写原型中的值。code

若是咱们在实例中添加了一个与实例原型中的一个属性同名的属性,那么该属性将会屏蔽原型中的那个属性。对象

 1 function Person(){
 2 }
 3 
 4 Person.prototype.name = "Nicholas";
 5 Person.prototype.age = 29;
 6 Person.prototype.job = "Software Engineer";
 7 Person.prototype.sayName = function(){
 8     alert(this.name);
 9 };
10 
11 var person1 = new Person();
12 var person2 = new Person();
13 
14 person1.name = "Greg";
15 alert(person1.name);   //"Greg" – from instance
16 alert(person2.name);   //"Nicholas" – from prototype

(1)更简单的原型语法

 1 function Person(){
 2 }
 3 
 4 Person.prototype = {
 5     name : "Nicholas",
 6     age : 29,
 7     job: "Software Engineer",
 8     sayName : function () {
 9         alert(this.name);
10     }
11 };
12 
13 var friend = new Person();
14 
15 alert(friend instanceof Object);  //true
16 alert(friend instanceof Person);  //true
17 alert(friend.constructor == Person);  //false
18 alert(friend.constructor == Object);  //true

注意:blog

这种方法下,constructor属性再也不指向Person。每建立一个函数,就会同时建立它的prototype对象,这个对象也会自动得到constructor属性。而咱们在上面代码使用的语法,本质上彻底重写了默认的prototype对象,所以constructor属性也就变成了新对象的constructor属性(指向object构造函数),再也不指向Person函数。ip

若是constructor的值真的很重要,能够像下面这样特地将它设置回适当的值。

1 Person.prototype = {
2  constructor : Person, 3     name : "Nicholas",
4     age : 29,
5     job: "Software Engineer",
6     sayName : function () {
7         alert(this.name);
8     }
9 };

注意:以这种方式设置constructor属性会致使它的[[Enumerable]]特性被设置为true。默认状况下,原生的constructor属性是不可枚举的。所以若是你使用兼容ECMA Script 5的Javascript引擎,能够试一试Object.defineProperty()。

1 //重设构造函数,只适用于ECMAScript 5兼容的浏览器
2 Object.defineProperty(Person.property, "constructor", {
3     enumerable: false,
4     value: Person
5 });

(2)原型的动态性

因为在原型中查找值的过程是一次搜索,所以咱们对原型对象所作的任何修改都可以当即从实例上反映出来——即便是先建立了实例后修改原型也照样如此。

1 var friend = new Person();
2 
3 Person.prototype.sayHi = function(){
4     alert("hi");
5 };
6 
7 friend.sayHi();   //"hi" (没有问题!)

但若是是重写整个原型对象,那么状况就不同。咱们知道,调用构造函数时会为实例添加一个指向最初原型的[[Prototype]]指针,而把原型修改成另外一个对象就等于切断了构造函数与最初原型之间的联系。

 1 function Person(){
 2 }
 3 
 4 var friend = new Person();
 5         
 6 Person.prototype = {
 7     constructor: Person,
 8     name : "Nicholas",
 9     age : 29,
10     job : "Software Engineer",
11     sayName : function () {
12         alert(this.name);
13     }
14 };
15 
16 friend.sayName();   //error

原型模式的缺点是共享性

 1 function Person(){
 2 }
 3 
 4 Person.prototype = {
 5     constructor: Person,
 6     name : "Nicholas",
 7     age : 29,
 8     job : "Software Engineer",
 9     friends : ["Shelby", "Court"],
10     sayName : function () {
11         alert(this.name);
12     }
13 };
14 
15 var person1 = new Person();
16 var person2 = new Person();
17 
18 person1.friends.push("Van");
19 
20 alert(person1.friends);    //"Shelby,Court,Van"
21 alert(person2.friends);    //"Shelby,Court,Van"
22 alert(person1.friends === person2.friends);  //true

四、组合使用构造函数模式和原型模式

构造函数模式用于定义实例属性,而原型模式用于定义方法和共享的属性。

 1 function Person(name, age, job){
 2     this.name = name;
 3     this.age = age;
 4     this.job = job;
 5     this.friends = ["Shelby", "Court"];
 6 }
 7 
 8 Person.prototype = {
 9     constructor: Person,
10     sayName : function () {
11         alert(this.name);
12     }
13 };
14 
15 var person1 = new Person("Nicholas", 29, "Software Engineer");
16 var person2 = new Person("Greg", 27, "Doctor");
17 
18 person1.friends.push("Van");
19 
20 alert(person1.friends);    //"Shelby,Court,Van"
21 alert(person2.friends);    //"Shelby,Court"
22 alert(person1.friends === person2.friends);  //false
23 alert(person1.sayName === person2.sayName);  //true

这种构造函数与原型混成的模式,是目前在ECMAScript中使用最普遍、认同度最高的一种建立自定义类型的方法。能够说,这是用来定义引用类型的一种默认模式。

五、动态原型模式

 1 function Person(name, age, job){
 2 
 3     //properties
 4     this.name = name;
 5     this.age = age;
 6     this.job = job;
 7     
 8     //methods
 9     if (typeof this.sayName != "function"){ 10     
11         Person.prototype.sayName = function(){ 12             alert(this.name); 13  }; 14         
15  } 16 }
17 
18 var friend = new Person("Nicholas", 29, "Software Engineer");
19 friend.sayName();

加粗代码只会在初次调用构造函数时才会执行。这里对原型所作的修改,可以当即在全部实例中获得反映。

六、寄生构造函数模式

这种模式的基本思想是建立一个函数,该函数的做用仅仅是封装建立对象的代码,而后在返回新建立的对象;但从表面上看,这个函数又很像是典型的构造函数。

 1 function Person(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 
12 var friend = new Person("Nicholas", 29, "Software Engineer");
13 friend.sayName();  //"Nicholas"

七、稳妥构造函数模式

所谓稳妥对象,指的是没有公共属性,并且其方法也不引用this的对象。

稳妥构造函数遵循与寄生构造函数相似的模式,但有两点不一样:

  • 新建立对象的实例方法不引用this;
  • 不使用new操做符调用构造函数。
 1 function Person(name, age, job){
 2 
 3     //建立要返回的对象
 4     var o = new Object();
 5 
 6     //能够在这里定义私有变量和函数
 7 
 8     //添加方法
 9     o.sayName = function(){
10         alert(name);
11     };
12 
13     //返回对象
14     return o;
15 }
相关文章
相关标签/搜索