Javascript创建对象时的几种模式浅析

Javascript创建对象时的几种模式浅析

前言:虽然创建对象时使用new Object()构造函数或者对象字面量都可以,但是这些方式有一个明显的缺点:在项目工程中,大量地、重复地创建对象时,会产生大量冗余代码。为了解决这个问题,javascript的开发者开始借鉴其他面向对象语言的设计模式。

1.工厂模式

  工厂模式是软件工程领域一种广为人知的设计模式,这种模式抽象了创建对象的具体过程,封装性比较好,外部调用构造函数不需使用new操作符,因为其内部就创建并返回了对象

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(“Nic”,21,”Font-end”);

var person2=createPerson(“Judy”,20,”Doctor”);

函数createPerson()能够根据接受的参数来构建一个包含所有必要信息的Person对象。可以多次地调用这个函数,来返回调用传入参数值不同则属性也不同的对象。工厂模式解决了创建多个相似对象的问题,但却没有解决对象识别的问题(即怎样知道一个对象的类型)。这时就有了下面的新模式。

2.构造函数模式

  在使用new操作符创建ObjectArray时,会调用原生的构造函数。此外,我们可以创建自定义的构造函数,从而定义自定义对象类型的属性和方法。例如,可以使用构造函数模式将上面的例子重写如下:

function Person(name,age,job){

  this.name=name;

  this.age=age;

  this.job=job;

  this.sayName=function(){

  alert(this.name);

}

}

var person1=new Person(“Nic”,21,”Font-end”);

var person2=new Person(“Judy”,20,”Doctor”);

这个例子中的Person()函数和前者的createPerson()函数存在如下不同:

没有显示地创建对象;

直接将属性和方法赋给了this对象;

没有return语句;

此外,为了显示地区分构造函数,函数名Person大写P开头,这是一种书写习惯,而非构造函数名一般小写开头。

注意

构造函数与其他函数的唯一区别,就在于调用它们的方式不同。

直接不使用new调用构造函数,属性和方法将添加给this指向的对象,也就是函数所在作用域的对象。(this可能指向window)。

要创建Person的新实例,必须使用new操作符。以这种方式调用构造函数实际上会经历以下4个步骤:

①创建一个新对象;

②将构造函数的作用域赋给新对象(this就指向了这个新对象);

③执行构造函数中的代码(为这个新对象添加属性);

④返回新对象。

创建出来的两个Person实例的对象,都有一个constructor属性(这个属性其实是原型对象继承来的)指向它们的构造函数Person

person1.cunstructor==Person;//true

person2.cunstructor==Person;//true

person1 instanceof Person;//true

person2 instanceof Person;//true

于是就解决了工厂模式不能识别对象类型的痛点,自定义构造函数可以将它的实例对象标识为一种特定的类型。

构造虽然好用,却也不是没有缺点。使用构造函数的主要问题是:不能共享方法,而是要反复同新对象一起新建同机制的自定义函数。

虽然不同实例同名方法的逻辑等价,但是它们却不想等

alert(person1.sayName == person2.sayName);//false

好在,这些问题可以通过原型模式解决。

3.原型模式

在展开原型模式介绍之前,我们需要了解,我们创建的每个函数都有一个prototype(原型)属性,这个属性是一个指针,指向一个对象,而这个对象的用途是包含可以由特定类型的所有实例共享的属性和方法,但是我们一般是公用方法共享,部分属性共享,自有属性不共享。

function Person(){

Person.prototype={

  constructor:Person,// 重定位被重写的原型指针指回构造函数

  name’Nic’,

  age:’29’,

  job:’Doctor’

  sayName:function(){

  alert(this.name);

}

}

var person1=new Person();

var person2=new Person();//完全共享属性和方法

原型模式也不是没有缺点的。首先,它省略了为构造函数传递初始化参数这一环节,实例对象无法定制不同的属性,修一个实例的属性,就是改动了引用的原型对象的属性。其最大的问题其实是由其完全的共享本性导致的。

4.组合使用构造函数和原型模式

  创建自定义类型的最常见方式,就是组合使用构造函数模式与原型模式。构造函数模式用于定义实例属性,而原型模式用于定义方法和共享的属性。结果,每个实例都会有自己的独有的实例属性,但同时又共享着对方法的引用,最大限度地节省了内存。这种模式支持向构造函数传递参数:

function Person(name,age,job){

  this.name=name;

  this.age=age;

  this.job=job;

}

Person.prototype={

constructor:Person,

sayName:function(){

alert(this.name);

}

}

var person1=new Person(“Nic”,21,”Font-end”);

var person2=new Person(“Judy”,20,”Doctor”);

这样构造函数与原型混成的模式,除了方法外露,封装性稍差之外,是js中使用比较广泛的一种创建自定义类型的方法。

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 p1=new Person(‘Nic’,29,’Doctor’);

p1.sayName();

  注意:使用动态原型模式时,不能使用对象字面量重写原型。如果在已经创建了实例的情况下重写原型,那么就会切断现有实例与新原型之间的联系。

5.寄生构造函数模式

除了使用new操作符并把使用的包装函数叫做构造函数之外,这个模式跟工厂模式其实是一模一样的。

这个模式可以在特殊情况下用来为对象创建构造对象,在不扩展原生构造函数的情况下自定义一个扩展型的构造函数。相对于在构造函数外部创建对象它的优势就是封装性比较好。

关于寄生构造函数模式:返回的对象与构造函数或者与构造函数的原型属性之间没有关系,不能依赖instanceof操作符来确定对象类型。所以,在可以使用其他模式的时候,一般不会使用寄生构造函数模式。

6.稳妥构造函数模式

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

function Person(name,age,job){

  var o=new Object();

    o.sayName=function(){

    alert(name)

}

return o

}

var p1=Person(‘Nic’,21,’Doctor’);

p1.sayName();

注意:在这种模式创建的对象中,除了使用sayName()方法之外,没其他办法访问name的值。即使有其他代码给这个对象添加方法或数据成员,但也不可能通过不调用sayName()方法来访问到传入到构造函数中的原始数据。稳妥构造函数模式提供这种安全性,使得它非常适合在某些安全执行环境下使用。

另外,与工厂模式和寄生构造函数模式类似,使用稳妥构造函数模式创建出来的对象与构造函数之间也没有什么关系,因此instanceof操作符对这种对象也没有意义。

 

参考:Javascript高级程序设计(第3版)



回复列表



回复操作






发布时间:2016-04-01 11:32:45

修改时间:2016-04-01 11:32:45

查看次数:541

评论次数:5

TA的文章总数

37