前言
对象字面量以及Object构造函数均可以创建单个对象,但是这样的方式有一个致命缺点,会产生大量的重复代码,为了解决这个问题,人们开始使用工厂模式创建对象。
工厂模式
工厂模式抽象化了创建具体对象的过程,ES5中无法创建类,开发人员就创建了一种函数,用函数来封装创建对象的细节。
function createPerson(name,age){
var obj = new Object();
obj.name = name;
obj.age = age;
obj.sayName() = function(){
console.log(this.name);
}
return obj;
}
var person1 = createPerson("James",28);
var person2 = createPerson("Harden",18);
从上可以看出,虽然工厂模式解决了创建多个相似对象的问题,但是没有解决对象识别的问题,即如何判别一个对象的类型。随着JS的不断壮大,一种新的模式出现了。
构造函数模式
通过Object和Array这样的原生构造函数可以创建特定类型的对象。受此启发,我们是不是可以构建自定义的构造函数,从而定义自定义类型的属性和方法?答案是肯定的。
function Person(name,age) {
this.name = name;
this.age = age;
this.sayName = function(){
console.log(this.name);
}
}
var person1 = new Person("James",28);
var person2 = new Person("Harden",18);
此模式与工厂模式的不同之处有:
- 没有显示的创建对象;
- 直接将属性和方法赋给了this对象;
- 没有return语句。
此外,按照惯例,构造函数都应该以一个大写的字母开头。
使用new操作符调用构造函数实际上经历的步骤为:
- 创建一个新对象;
- 将构造函数的作用域赋给新对象(因此this指向了这个新对象);
- 执行构造函数中的代码(为新对象添加属性);
- 返回新的对象。
上述例子中的两个实例对象都有一个constructor(构造函数)属性,该属性指向构造函数Person。
console.log(person1 instance of Object); //true
console.log(person1 instance of Person); //true
console.log(person2 instance of Object); //true
console.log(person2 instance of Person); //true
上述代码使用** instanceof**操作符可验证person1和person2既是Object的实例,也是Person的实例。
创建自定义的构造函数意味着可以将它的实例标识为一种特定的类型,这正是构造函数模式胜过工厂模式的地方。
构造函数的问题
使用构造函数模式的主要问题在于:每个方法要在每个实例上重新创建一遍。从逻辑角度来说,此时的构造函数可以这样定义:
function Person(name,age) {
this.name = name;
this.age = age;
this.sayName = new Function("console.log(this.name)");
}
console.log(person1.sayName === person2.sayName) //false
说明白些,以这种方式创建函数,会导致不同的作用域链和标识符解析。上述代码说明不同实例上的同名函数式不等的。
然而,创建两个完成相同任务的Function实例确实没必要。因此,我们可以这么做:
function Person(name,age) {
this.name = name;
this.age = age;
this.sayName = sayName;
}
function sayName(){
console.log(this.name);
}
这样实例就可共享在全局作用域中定义的sayName函数。这样确实解决了上述问题,但是新问题又来了:r如果对象需要定义很多方法,那么就要定义很多个全局函数,那我们自定义的封装性可言了。而这些问题可以通过原型模式来解决。