前面已经对JavaScript的面向对象程序设计作了简单的介绍,包括了对象的属性、对象的工厂模式、构造函数和原型等。通过介绍,这些创建对象的方法依然有不少优化和改进的地方。
组合使用构造函数模式和原型模式
创建自定义类型的最常用方式就是使用组合构造函数和原型模式。构造函数用于定义实例属性,原型用于定义共享的属性和方法。每个实例都有自己的实例副本,同时又共享了原型属性和方法,节省了内存。还支持向函数传递参数。
1 function Person(name,age,sex){ 2 this.name=name; 3 this.age=age; 4 this.sex=sex; 5 } 6 Person.prototype={ 7 constructor:Person, 8 getName:function(){ 9 return this.name; 10 } 11 } 12 var person1 = new Person("jack",18,"man"); 13 var person2 = new Person("helen",19,"woman"); 14 console.log(person1.getName());//jack 15 console.log(person2.getName());//helen 16 console.log(person1.getName===person2.getName);//true
上面的代码中,实例属性都是在构造函数中定义的。在原型中定义了contructor和getName方法,原型中的方法由所有的实例共享。14行和15行输出的结果不相同,因为实例属性定义再构造函数中,而16行输出true,则证明两个实例的getName指向同一个栈内存。
动态原型模式
上面的例子中,我们将函数的声明和原型的定义是分开的。为了解决这一点,我们可以在构造函数中初始化原型。
1 function Person(name,age,sex){ 2 this.name=name; 3 this.age=age; 4 this.sex=sex; 5 if(typeof this.getName!="function"){ 6 Person.prototype.getName=function(){ 7 return this.name; 8 } 9 } 10 } 11 var person1 = new Person("jack",18,"man"); 12 var person2 = new Person("helen",19,"woman"); 13 console.log(person1.getName());//jack 14 console.log(person2.getName());//helen 15 console.log(person1.getName===person2.getName);//true
上面的代码中,我们在构造函数中声明了属性以及原型的方法。但是我们在5行有判断,只有当函数不存在的时候才调用,避免了函数的多次调用。
寄生构造函数模式
通常情况下,我们使用上面的几种模式已经可以满足多种创建对象的需求了。JavaScript还为我们提供了寄生构造函数模式。这种模式的基本思想是创建一个函数,该函数仅仅是用来封装对象的代码,并返回创建的对象。
1 function Person(name,age,sex){ 2 var obj =new Object(); 3 obj.name=name; 4 obj.age=age; 5 obj.sex=sex; 6 obj.getName=function(){ 7 return this.name; 8 } 9 return obj; 10 } 11 var person = new Person("jack",18,"man"); 12 console.log(person.getName());//jack
上面的代码使用寄生构造函数模式创建了对象person,上面的对象,除了用new 来构造对象外,其他的与工厂模式创建对象的过程是一致的。通过寄生构造函数模式创建的对象,与构造函数本身并没有关系,构造函数返回的对象与在构造函数创建的对象并没有关系。不能通过instanceof来确定对象的类型。