在Javascript中,创建一个对象可以用Object构造函数或者对象字面量,但是,如果要创建很多对象,这就会产生大量的代码,很繁琐。所有,就有了以下的创建对象的设计模式。
一、工厂模式
- 工厂模式:用函数封装特定接口对象细节,返回一个对象,来看一个例子:
function person(name,age){
var o = new Object();
o.name = name;
o.age = age;
o.sayName = function () {
alert(this.name)
}
return o
}
var person1 = person("wahaha",11)
person1.sayName(); //wahaha
alert(person1 instanceof person); //false
问题来了,person1不属于person类,没有了对象识别,因为内部是返回来Object类型对象。所以为了解决这个,大牛们又发明了构造函数模式
二、构造函数模式
- 构造函数模式没有了return返回值和new一个Object,和用this指向当前的作用域,下面来看个例子:
function Person(name,age){
this.name = name;
this.age = age;
this.sayName = function () {
alert(this.name)
}
}
var person1 = new Person("wahaha",11)
person1.sayName(); //wahaha
alert(person1 instanceof Object); //true
构造函数模式改正了工厂模式的确定,让person1有了对象识别,并且用了new关键字创建,所以有了一下引声:
- 每个实例都有一个constructor属性,这属性指向对象person的
- 将构造函数当作函数使用:可以call()和apply()来改变作用域
构造函数模式也不是没有缺点,它里面的方法不能共享,每一个实例都有相同的方法,这样很耗效率,所以有人又发明了原型模式
三、原型模式
- 原型模式:创建的每个函数都会有一个prototype属性,这属性是个指针,指向对象。这属性是每个对象的实例共享的对象。来看下代码:
function Person(){
}
Person.prototype.name = 'wahaha';
Person.prototype.age = 29;
Person.prototype.sayName = function () {
alert(this.name)
}
var person1 = new Person();
var person2 = new Person();
alert(person1.sayName == person2.sayName); //true
这样就能共享一个方法了,这里还有一些知识需要说下:
- 每个实例都有一个[[prototype]]属性,这属性没有标准访问,但是浏览器都支持这个属性。这属性 指向的是原型:Person.prototype,知直接指向原型,和构造函数没关系。
- 原型都有一个constructor属性指向对象Person。
- 每个实例需要属性或方法时候都会进行两次搜索。注意的是:原型里面的属性的值不能通过实例相同属性来改变,并且如果实例上有相同属性名则会屏蔽原型的属性。
这又有一个问题:如果过原型里面需要很多属性和方法,则会很繁琐,所以用对象字面量可以重写原型
function Person(){
}
Person.prototype = {
name : 'wahaha',
age : 11,
sayName :function () {
alert(this.name)
}
}
var person1 = new Person();
person1.sayName(); //wahaha
但是重写了原型,constructor的指向不再是Person,所以要在对象字面量中加多一条语句指向Person,
constructor:Person,
注意的是:实例的[[prototype]]属性的值都改变了,是指向Person.prototype,但是不是指向重写的Person.prototype
下面还有一些关于原型模式用到的方法:
1.isPrototypeof():确定实例是否与原型对象存在这种关系
alert(Person.prototype.isPrototypeOf(person1)) //true
2.getPrototypeof (Object);返回实例[[prototype]]的值
3.hasOwnPrototype():判断一个属性或者方法是否存在于实例上
alert(person1.hasOwnProperty('name')) //false,存在则true
4.Object.keys(原型对象):列出可以枚举的属性(包括实例和原型)
5.Object.getOwnPrototypeNames(原型对象):列出所有属性(包括实例和原型)
这里顺便写一下[[Enumerable]]这属性吧
[[Enumerable]]是标记原型属性是否可枚举,constructor属性默认是false。
原型模式也有缺点:属性是数组的话就不能了,因为每个实例push一个进去,其他实例都受影响。
四、组合使用构造函数模式和原型模式
这是最佳方案:构造函数定义实体自己特有的方属性,原型模式用来定义全部实例共有的属性和方法
五、动态原型模式
动态原型模式是在原型初始化的时候,把方法或者属性写进原型
function Person(name,age){
this.age = age;
this.name = name;
if(typeof this.sayName != "function"){
Person.prototype.sayName = function () {
alert(this.name)
}
}
}
var person1 = new Person("wahaha",11);
person1.sayName();
六、稳妥构造函数模式
如果考虑安全性的话用这函数模式,因为不可以用new和this