在学习设计模式之前,我们首先要确定几个要点,应该从哪几个方向研究设计模式,当我们搞清楚这个设计模式后,我们应该在什么场合使用这个模式:
- 模式的名称和实现方式;
- 模式的适用场景,即这个模式是为了解决什么问题而设计出来的;
- 模式的整体结构是什么,我该怎么使用(有的模式有改进型,混合型等各种变形);
- 该模式与其他模式的比较
- 总结,模式的优点与缺点
下面我们就根据这几个点开始学习
1. 模式的名称和实现方式 #
本文研究的是构造函数模式,名称当时构造函数(Constructor)模式了。构造器模式用于创建特定类型的对象——准备好对象以备使用,同时接受构造函数可以使用的参数,以在第一次创建对象时,设置成员属性和方法的值。
既然是构造函数模式,那就必然是以构造函数为基础的模式。如Array(), String(), Object()等js本来提供的构造函数,也是能形成构造函数模式的。
var arr = new Array(1, 2, 3, 4);
var str = new String('this is a string');
var obj = new Object({
'name':'wenzi',
'age':'25',
'sex':'male'
})
当然,js里提供了这些构造函数的字面量声明的简单方式,不用再那么麻烦的new
了,而且更加推荐使用以下的方式进行构建。
var arr = [1, 2, 3, 4];
var str = 'hello world';
var obj = {
'name':'wenzi',
'age':'25',
'sex':'male'
}
同时,我们也可以使用自己定义的构造函数,一般构造函数的首字母大写,这是一种良好的编码规范。如:
function Person(name, age){
this.name = name;
this.age = age;
this.getName = function(){
return this.name;
}
}
var parent = new Person('jack', 46);
var son = new Person('jack', 21);
2. 模式的适用场景 #
构造函数模式通常使用在对象的属性较少,相对来说比较简单的地方,比如仅仅需要包装出一个对象,或者想用一个变量存储几个属性。
3. 模式的整体结构 #
模式的整体刚才在第1部分讲解了一下,主要有:原生构造函数,对象字面量,自定义构造函数等。
在上面的自定义构造函数中,我们可以确认parent和son两个属性值的比较结果
parent.name === son.name; // true
parent.age === son.age; // false
可是parent.getName===son.getName
会输出什么呢?运行之后,我们就会发现,这个比较式会输出false。为什么呢?函数名一样,都是getName,而且函数体也一样,可是为什么会输出false呢?
其实,每次在使用Person进行对象创建的时候,都会创建一个新的函数给getName,因此,虽然看起来是一样,但实际上是两个函数。如果创建的对象比较多的话,那么创建的getName函数也会比较的多,可是getName的功能是一样的,没必要跟着也创建好多次。因此我们可以做如下的改进:
function Person(name, age){
this.name = name;
this.age = age;
this.getName = getName;
}
function getName(){
return this.name;
}
var parent = new Person('jack', 46);
var son = new Person('jack', 21);
parent.getName === son.getName; // true
这样使用 Person 创建的对象使用getName时,都会将引用指向getName()这个函数。因此,上面的比较式会输出出。这样写能够减少getName的创建次数,减少内存的使用。不过其实这种方式,也是有缺点的,如果构造函数里的方法很多呢,那是不是得写好多全局的函数,用来引用呢。还有一种方式是,把方法写在原型prototype上,调用js构造器创建一个对象后,新对象就会具有构造器原型的所有属性。
function Person(name, age){
this.name = name;
this.age = age;
}
Person.prototype.getName = function(){
return this.name;
}
var parent = new Person('jack', 46);
var son = new Person('jack', 21);
parent.getName === son.getName; // true
针对这三种方式,应该考虑实际情况使用,而不是一味的采用第三种方式。如果构造函数相对来说比较简单,而且创建出的对象也不多,也就没比较使用原型。
4. 总结 #
为什么没有和其他模式的比较呢?因为还没有开始其他模式的讲解,哈哈!
构造函数模式的优点就是构造简单,使用方便,而且容易理解。
当然缺点也是比较明显的,就是扩展性差,每个实例的方法都是独立的,多数情况下同个对象的实例方法都是一样的。