工厂模式:
抽象了创建具体对象的过程,用函数来封装创建对象的细节。
缺点:虽然方便了创建多个对象的问题,但却没有解决对象识别的问题。(无法知道一个对象的类型,都是Object类型)
(js 工厂模式里面,所有的函数都只是Object的实例,这样的判断没有多大的意义; 而在构造函数里面,构造出来的函数不仅是Object的实例,也是构造函数的实例,而构造函数是我们自己定义的,相当于我们自己定义了一个新的对象类型,可以识别的新的对象类型;)
//工厂方法创建对象
function createPersn(name,age,sex){
var obj=new Object();
obj.name=name;
obj.age=age;
obj.sex=sex;
return obj;
}
var person1=createPersn("chen",21,"man");
var person2=createPersn("zhang",21,"women");
构造函数模式:
- 没有显示地创建对象
- 直接将属性和方法赋给this对象
- 没有return语句
- 创建实例必须使用new操作符
构造函数创建对象经历的4个步骤:
- 创建一个新对象
- 将构造函数的作用域赋给新对象(改变this指向,创建实例时修改了作用域,指向新对象)
- 执行构造函数中的代码(给新对象添加属性)
- 返回新对象
优于工厂模式的地方:创建自定义的构造函数意味着将来可以将它的实例表示为一种特定的类型。
构造函数也是函数,构造函数与其他函数的唯一区别在于调用他们的方式不同。任何函数只要通过new操作符来调用,那他就可以用作为构造函数,而构造函数不通过new操作符来调用,那它跟普通函数没有两样。
缺点:每个实例都包含其对象的所有属性和方法,即不同实例上的同名属性和函数不是指向同一个内存(即属性和函数不会共享,各实例存有独立备份)
//构造函数模式创建对象
function createPersn(name,age,sex){
this.name=name;
this.age=age;
this.sex=sex;
}
var person1=new createPersn("chen",21,"man");
var person2=new createPersn("zhang",21,"women");
原型模式:
可以让所有对象实例共享它所包含的属性和函数。
原型对象:任何时候,只要创建了一个新函数,就会根据一组特定的规则为该函数创建一个prototype属性,该属性执行那个函数的原型对象。不能通过对象实例来重写原型属性和函数,因为新值会覆盖掉原型中的属性和函数,在当前实例对象中表现为局部变量(其他实例不受影响)
//原型模式创建对象
function Person(){}//空构造函数
Person.prototype.name="chen";
Person.prototype.age=21;
Person.prototype.sex="男";
var person1=new Person();
var person2=new Person();
//简便的原型语法——对象字面量,将原型属性封装在原型对象中
Person.prototype={
name:"chen",
age:29,
sex:"男"
}
function test(){} //空构造函数
var t1=new test();//不使用对象字面量方式,定义原型属性前创建的实例可访问原型链
test.prototype.getName=function(){console.log("name");};
t1.getName();//name
var t2=new test();//不使用对象字面量方式,定义原型属性后创建的实例可访问原型链
t2.getName();//name
重写原型对象切断了现有原型与任何现有对象实例之间的联系,即使用对象字面量方式改动的原型对象会导致在此之前创建的实例发生访问原型链错误的情况(使用对象字面量方式封装原型属性,之后对于该原型对象的改动将破坏原型链,在改动前创建的实例将引用之前的原型)。
function test(){} //空构造函数
test.prototype={getName:function(){console.log("oldName")}};//原来的原型对象
var t1=new test();//在定义原型对象前创建实例,无法访问新的原型对象的属性和函数,保持了旧原型对象的引用
test.prototype={//新的原型对象
getName:function(){console.log("newName")},
getElse:function(){console.log("else")}
};
t1.getName();//oldName
//t1.getElse();//Uncaught TypeError: t.getName is not a function
var t2=new test();//在定义原型对象后创建实例,可以访问原型对象的属性和函数
t2.getName();//newName
组合模式
很少有人单独使用原型模式,因为实例常常不需要共享全部属性和函数,只需要将需要共享的部分封装到原型对象中,其余的使用构造函数模式创建属于该实例自身的属性和函数,这种构造模式+原型模式创建对象的方式叫做组合模式
function Person(name,age,sex){
this.name=name;
this.age=age;
this.sex=sex;
}
Person.prototype={
constructor:Person,//指向构造函数
getName:function(){console.log(this.name);}
}
var person1=new Person("chen",21,"man");
var person2=new Person("zhang",21,"women");
person1.getName();//chen
person2.getName();//zhang
寄生构造函数模式
除了使用new操作符并把使用的包装函数叫做构造函数之外,这个模式跟工厂模式一模一样。
构造函数返回的对象与在构造函数外部创建的对象没有什么不同,因此不能依赖instanceof操作符来确定对象类型,因此不建议使用寄生构造函数模式
//寄生构造函数模式创建对象
function createPersn(name,age,sex){
var obj=new Object();
obj.name=name;
obj.age=age;
obj.sex=sex;
return obj;
}
var person1=new createPersn("chen",21,"man");
稳妥构造函数模式
稳妥对象指的是没有公共属性,且其他方法也不引用this的对象,适合放在一些安全环境中(如禁止使用this和new的环境)
与寄生构造函数模式的区别:1、新创建对象的实例方法不引用this 2、不使用new操作符调用构造函数
function Person(name,age,sex){
var obj=new Object();
obj.getName=function(){console.log(name);};
return obj;
}
var person1=Person("chen",21,"man");
person1.getName();//chen