最基本的对象创建方式是通过Object构造函数或对象字面量的方式创建:
①通过Object构造函数的方式创建对象:
1 var person=new Object();//或者写成var person={} 2 person.name='张三';
②通过对象字面量的方式创建对象:
1 var person={name:'张三'};
Object构造函数模式和对象字面量模式这两种创建对象的方式,都有明显的缺点:使用同一个接口创建很多对象的时候,会产生大量的重复代码。为解决这个问题,可以使用工厂模式。工厂模式就是以函数的方式来封装以特定接口创建对象的细节。
③以工厂模式的方式创建对象:
1 function createPerson(name,sex){ 2 var person={}; 3 person.name=name; 4 person.sex=sex; 5 return person; 6 } 7 8 var person=createPerson('张三','男');
工厂模式创建对象的方式虽然解决了重复代码的问题,但是还有一个重要的问题还有待解决,那就是对象识别的问题,就是所有以工厂模式创建对象的方式产生的对象都只能判断是Object类型,不能够进一步识别该对象。为了解决这一问题,我们可以使用构造函数模式。
④以构造函数模式的方式创建对象:
1 function Person(name,sex){ 2 this.name=name; 3 this.sex=sex; 4 } 5 var p=new Person('张三','男');
以构造函数模式创建对象的方式与以工厂模式创建对象的方式的不同之处在于:a、没有显示的创建对象;b、直接将属性和方法付给了this;c、没有return语句。同时要创建Person的实例,必须用new操作符。通过构造函数的方式创建的对象可以通过p.constructor==Person或者p instanceof Person来判定对象的类型。虽然以构造函数的方式创建对象解决了对象识别的问题,但是仍然有一些问题,就是该引用类型存在方法属性时,每个方法都要在每个实例上重新创建一遍。我们可以使用原型模式来解决这个问题。原型模式就是在构造函数的prototype上添加属性和方法,这些方法和属性是所有通过该构造函数生成的对象实例共享的。使用原型模式的好处就是可以让所有对象实例共享原型对象上的属性和方法。
⑤以原型模式的方式创建对象:
1 function Person(){ 2 } 3 4 Person.prototype.name='张三'; 5 Object.defineProperty(Person.prototype,'sex',{configurable:true,writable:true}); 6 7 var p=new Person();
以原型模式创建对象的最大缺点是,所有的属性都是所有实例共享的,因此任何一个实例的属性更改了,那么其他实例的该属性也会更改。为了解决这个问题,可以组合使用构造函数模式和原型模式。
⑥以组合使用构造函数模式和原型模式的方式创建对象:
1 function Person(name,age){ 2 this.name=name; 3 this.age=age; 4 } 5 6 Object.defineProperty(Person.prototype,'birth',{get:function(){ 7 return ((new Date()).getFullYear()-this.age); 8 }}) 9 10 var p=new Person('张三',25); 11 console.log(p.birth); //1991
一般的,把需要共享的属性和方法放在原型中,把不需要共享的属性放在构造函数中。
使用组合使用构造函数模式和原型模式的方式,构造函数和原型的代码是独立分开的,这会让人感觉很困惑,为了让两者放在一起,我们可以使用动态原型模式。
⑦以动态原型模式创建对象:
1 function Person(name,age){ 2 this.name=name; 3 this.age=age; 4 if(typeof getBirth !=='function'){ 5 Person.prototype.getBirth=function(){ 6 return ((new Date()).getFullYear()-this.age); 7 } 8 } 9 } 10 11 var p=new Person('张三',25);
原型对象上的方法只会在生成第一个实例的时候添加到原型对象上。
对于已有的引用类型,不能给该引用类型添加属性(如果直接添加在该引用类型的原型对象上,那么会影响所有的该引用对象生成的实例),比如说希望创建一个具有特殊方法的数组,由于不能直接修改Array构造函数,也不能给Array的原型对象上添加方法,我们可以使用寄生构造函数模式。寄生构造函数模式就是拥有构造函数的外表,构造函数内部用的是工厂模式的方式生成新的实例。
⑧以寄生构造函数的方式创建对象:
1 function SpecialArray(){ 2 var values=new Array(); 3 values.push.apply(values,arguments); 4 5 //添加特殊方法 6 values.toPipedString=function(){ 7 return this.join("|"); 8 } 9 10 return values; 11 } 12 13 var sa=new SpecialArray("张三","李四","王五"); 14 console.log(sa.toPipedString()); //张三|李四|王五
当我们希望我们创建的对象拥有私有变量,只有该对象的公有方法可以修改私有变量的值,我们可以使用稳妥构造函数模式。稳妥构造函数模式类似寄生构造函数模式,只是不将属性直接设置在对象实例上,而是通过闭包的方式,为对象实例添加可以访问私有属性的方法。
⑨以稳妥构造函数模式的方式创建对象:
1 function Person(name){ 2 var o =new Object(); 3 o.getName=function(){ 4 return name; 5 }; 6 7 return o; 8 } 9 10 var p=Person("张三"); 11 console.log(p.getName());//张三 12 console.log(p.name);//undefined
稳妥构造函数主要用于一些安全环境中(这些环境中会禁止使用new和this),或者用于防止数据被其他应用程序改动时使用。注意这里的引用类型的公有方法使用不能使用this.来取得私有属性。