工厂模式
我们很多时候会使用对象字面量来创建单个对象,但是这个方式有比较明显的缺点,创建类似对象是会产生很多重复代码,比如:
var person1 = { name: "nicolas", age: 29, job: "software engineer" }; var person2 = { name: "greg", age: 27, job: "doctor" }
每次创建一个新对象就需要重新输入每一个属性,为了解决这个问题,我们开始使用工厂模式的一种变体,使用一个函数来封装以特定接口创建对象的细节,如下:
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("nicolas", 29, "software enginner"); var person2 = createPerson("greg", 27, "doctor");
这个函数可以接收一些参数来构建一个包含必要信息的对象,可以无数次调用这个函数,而且每次都会返回一个包含三个属性一个方法的对象,工厂模式虽然解决了创建多个相似对象的问题,但是却没有解决对象识别的问题,person1和person2之间并没有内在的联系。因此我们引入了构造函数模式。
构造函数模式
构造函数也是函数,只是通过
new
操作符来调用,并且在内部直接将属性和方法绑定到this
变量上,我们可以把之前的例子重写如下:function Person(name, age, job) { this.name = name; this.age = age; this.job = job; this.sayName = function(){ alert(this.name); } } var person1 = new Person("nicolas", 29, "software engineer"); var person2 = new Person("greg", 27, "doctor");
我们可以看到构造函数中并没有显示的创建对象,也不存在return语句,同时按照惯例一般构造函数的函数名Person首字母使用大写。当使用
new
操作符调用时,实际上经历了以下四个过程:- 创建一个新对象;
- 将构造函数的this赋给新对象;
- 为这个新对象添加属性和方法;
- 返回新对象。
最后person1和person2都保存着Person的一个不同的实例,同时他们都一个
constructor
属性,指向Person
。构造函数虽然好,但是也有一个主要的缺点,那就是每个内部的同样方法都要在内存中重新创建一遍,比如上面的例子里的sayName方法,实际上每次定义一个函数都相当于每次都实例化了一个函数对象,他们在内存中保存于不同的地址,可以用以下代码证明不同实例上的同名函数其实并不相等:
alert(person1.sayName == person2.sayName); // false
其实大可以把函数定义转移到构造函数外部来解决这个问题:
function Person(name, age, job) { this.name = name; this.age = age; this.job = job; this.sayName = sayName; } } function sayName() { alert(this.name); } var person1 = new Person("nicolas", 29, "software engineer"); var person2 = new Person("greg", 27, "doctor");
这样以来,person1和person2就共享了全局作用域中的同一个sayName函数,但是新问题又来了:如果对象需要定义很多方法,那么就要定义很多个全局函数,那也就丝毫没有封装性可言了,好在,这些问题我们可以通过原型模式来解决。
创建对象的几种模式(一)