JavaScript 中虽然 Object 构造函数或对象字面量都可以用来创建单个对象,但是这些方法都有一个明显的缺点:使用同一个接口创建很多对象,会产生大量的重复代码。
0.对象字面量:
var person={
this.name="Sakura",
this.age=22,
this.job="前端开发",
this.sayName=function(){
console.log(this.name);
}
}
//当一个变量被封装在一个对象中时,这个变量就可以称为该对象的一个属性
//同样的,当一个函数被封装在一个对象中时,这个函数就可以称为该对象的一个方法
1.工厂模式
用函数来封装以特定接口创建对象的细节,这种模式抽离了创建对象的具体过程。
function creatPerson(name,age,job){
//创建一个对象 o
var o = new object();
o.name = name;
o.age = age;
o.job = job;
o.sayName = function(){
alert(this.name);
};
return 0;
}
//通过函数创建
var person1 = createPerson("gercke",20,"student");
var person2 = createPerson("qq",18,"student");
优点:解决了创建多个相似对象时,代码的复用问题
缺点:使用工厂模式创建的对象,无法知道一个对象的类型是什么
2.构造函数模式
构造函数可用来创建特定类型的对象.我们也可以创建自定义的构造函数,从而定义对象类型的属性和方法
function createPerson(name, age, job){
//通过this属性访问
this.name = name;
this.age = age;
this.job = job;
this.sayName = function(){
alert(this.name);
};
return o;
}
var person1 = new createPerson("gercke",20,"student");
var person2 = new createPerson("qq",18,"student");
构造函数实例化一个对象的时候,对象中会包含一个 proto 属性指向构造函数原型对象,而原型对象中则包含一个 constructor 属性指向构造函数。因此在实例对象中我们可以通过原型链来访问到 constructor 属性,从而判断对象的类型。
优点:解决了工厂模式中对象类型无法识别的问题,并且创建自定义的构造函数意味着将来可以将它的实例标识为一种特定的类型。
缺点: ECMAScript 中的函数是对象,在使用构造函数创建对象时,每个方法都会在实例对象中重新创建一遍。拿上面的例子举例,这意味着每创建一个对象,我们就会创建一个 sayName 函数的实例
3.原型模式
创建的每一个函数都有一个 prototype 属性,这个属性指向函数的原型对象,这个对象的用途是包含可以由特定类型的所有实例共享的属性和方法
function Person(){
}
Person.prototype.name = "james";
Person.prototype.age = 9;
Person.prototype.job = "student";
Person.prototype.sayName = function(){
alert(this.name);
}
var person1 = new Person();
person1.sayName(); // "james"
var person2 = new Person();
person2.sayName(); // "james"
console.log(person1.sayName === person2.sayName) // true
与构造函数模式不同的是,原型对象上的属性和方法是有所有实例所共享的
优点:解决了构造函数模式中多次创建相同函数对象的问题,所有的实例可以共享同一组属性和函数。
缺点: ①实例在默认情况下都是默认的属性值;②对于引用类型的值会有冲突
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 createPerson("gercke",9,"student");
var person2 = new createPerson("qq",9,"student");
console.log(person1.name); // "gercke"
console.log(person2.name); // "qq"
console.log(person1.sayName === person2.sayName); // true
优点:结合两种,可以知道实例的类型,同时所有实例之间可以共享同一属性和函数。
缺点:由于使用了两种模式,因此对于代码的封装性来说不是很好。
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 person1 = new createPerson("gercke",9,"student");
person1.sayName(); // "gercke"
优点:解决了组合模式中封装性的问题
6.寄生构造函数模式
这种模式的基本思想是创建一个函数,该函数的作用仅仅是封装创建对象的代码,然后返回新创建的对象。
function Person(name, age, job){
var o = new Object();
o.name = name;
o.age = age;
o.job = job;
o.sayName = function(){
alert(this.name);
};
return o;
}
//工厂模式最后创建没有使用new
var person1 = new Person("james",9,"student");
其实这个模式和工厂模式基本上是一摸一样的,只不过我们是采用 new 操作符最后来创建对象。
优点:扩展对象时,不需要修改原来的构造函数
缺点:和工厂模式一样的问题,不能依赖 instanceof 操作符来确定对象的类型
7.稳妥构造函数模式
所谓稳妥对象,指的就是,没有公共属性,而且其方法也不使用 this 的对象。稳妥对象最适合在一些安全的环境中(这些环境中会禁止使用 this 和 new),或者在防止数据被其他应用程序改动时使用。
function Person(name, age, job){
//创建要返回的对象
var o = new Object();
//可以在这里定义私有变量和函数
o.name = name;
//添加方法
o.sayName = function(){
console.log(this.name);
}
//返回对象
return o;
}
var person1 = Person("gercke",19,"student");
person1.sayName(); // "james"
优点:以上面为例,除了 sayName 方法外,没有别的方法可以访问数据成员,这就是稳妥构造函数提供的安全性。
缺点:和寄生构造函数一样,没有办法使用 instanceof 操作符来判断对象的类型