面向对象语言有一个非常显著的标志,那就是它们都有类的概念,通过类之间的继承就可以达到任意创建具有相同属性方法的对象。而在ECMAScript中并没有类的概念,它把对象定义为:无序属性的集合,其属性包含基本值、对象或者函数。这也意味着对象的每个属性或者方法都有一个名字,每个NAME对应一个VALUE,我们可以通过这些NAME来访问对应的VALUE。那么我们怎么创建这些对象呢?下面具体阐述一下七中对象创建方法。
1.工厂模式
工厂模式是软件工程领域一种广为人知的设计模式,这种模式抽象了创建具体对象的过程。考虑到ECMAScript中并没有类的概念,开发人员就采用创建一个函数,再用这个函数以特定接口创建对象的细节。示例如下:
function createPerson(name,age,job){ var p=new Object(); p.name=name; p.age=age; p.job=job; p.sayAge=function(){ alert(this.age); }; return p; }var person=createPerson(“syf”,“23”,“stu”);
2.构造函数模式
ECMAScript中有一些原生的构造函数,例如Object,Arrary等,我们可以用它们创建对象。除此之外,我们也可以创建自定义的构造函数,来定义自定义对象类型的属性和方法。示例如下:
function Person(name,age,job){ this.name=name; this.age=age; this.job=job; this.sayAge=function(){ alert(this.age) ; }; } var person=new Person("syf","23","stu");
通过对比工厂方式和构造函数方式可以看出,它们函数内部的代码有很大的相似性。但在构造函数模式里我们并没有显示的创建对象(new Object),而是直接将属性和方法赋值给了this对象,也没有使用return语句来返回结果。要想通过构造函数模式创建对象的话,那就得使用new操作符。
构造函数与其他函数的唯一区别就是调用它们的方式不同,任何函数只要通过new操作符调用,那么它就可以作为构造函数,反之则是普通函数。
3.原型模式
每个函数都有一个prototype属性,它是一个指针,指向一个对象,这个被指向的对象包含着可以被特定类型的所以实例共享的属性和方法。从字面来理解,prototype就是通过调用构造函数而创建的那个对象实例的原型对象,里面包含着对象实例共享的属性和方法。换句话说,不必在构造函数中定义对象实例的信息,而是可以将这些信息直接添加到原型对象中。示例如下:
function Person(){} Person.prototype.name="syf"; Person.prototype.age="23"; Person.prototype.job="job"; Person.prototype.sayAge=function(){ alert(this.age); }; var person= new Person();
由原型模式创建的对象访问的都是同一组属性和方法,所有实例在默认情况下都取得相同的属性值。对象实例可以访问原型中的属性和方法,但不能重写原型中的值。如果再实例中添加了一个与原型中同名的属性,那将在实例中创建这个属性,这个新的属性也会屏蔽掉原型中的属性和方法。这个得属性或者方法即使设置为null,也不会再恢复与原型的链接,除非我们使用delete操作符完全删除实例属性,这样才能恢复对原型中属性的访问。也可以理解为,通过原型模式创建对象实例,实例是原型的一个映射,修改实例不会反映到原型上,而原型上的改动会对实例产生影响。
4.组合使用构造函数模式和原型模式
在创建自定义类型的众多方式中,组合方式是最常用的方式。这是因为在组合模式中,我们用构造函数来定义实例属性,用原型模式来定义方法和共享的属性,最终每个实例都有自己的一份实例属性副本,同时还有共享的方法,大大节省了内存。示例如下:
function Person(name,age,job){ this.name=name; this.age=age; this.job=job; this.friends=["zz","fzw"]; }Person.prototype={ constructor:Person; sayAge:function(){ alert(this.age); }}var person=new Person("syf","23","stu");
5.动态原型模式
其本质上就是在需要的时候或者说在必要的情况下,才通过构造函数初始化原型。也可以这样理解,可以通过检查某个应该存在的方法是否有效,来决定是否初始化原型。它把所有的信息都封装在了构造函数中,也保证了使用组合模式创建对象的优势。示例如下:function Person(name,age,job){
function Person(name,age,job){
this.name=name; this.age=age; this.job=job; if(typeof this.sayAge !="function"){ Person.prototype={ sayAge:function(){ alert(this.age);} } } var person=new Person("syf","23","stu");
6.寄生构造函数模式
其基本思想是创建一个函数,这个函数只用来封装创建对象的代码,然后返回创建的对象。这种方法除了使用NEW操作符创建对象之外,与工厂模式相比并没有什么区别。
function Person(name,age,job){ var p=new Object(); p.name=name; p.age=age; p.job=job; p.sayAge=function(){ alert(this.age); }; return p; } var person= new Person(“syf”,“23”,“stu”);
用寄生构造函数创建对象时,其返回的对象与构造函数或者与构造函数的原型属性之间没有关系。构造函数返回的对象与在构造函数外创建的对象没有什么不同,也就不能使用instanceof操作符来确定对象类型。