ES5中没有类的概率,我们要创建许多同类型对象时需要使用其他方法来实现
工厂模式
工厂模式其实就是通过定义一个普通函数,利用函数来创建对象,对象的属性都是通过函数参数传递的
function createPerson(name,age,job){
var o=new Object();
o.name=name;
o.age=age;
o.job=job;
o.sayName=function(){
alert(this.name);
}
}
var person1=createPerson('zzh',21,'student');
var person2=createPerson('ly',22,'student');
优点:简单。通过函数封装代码,减少了代码量
缺点:通过函数创建的对象并不能被识别是哪一类型的对象(不能用instanceof检测出具体对象类型)。
构造函数模式
我们创建的数组或对象其实都是通过构造函数来创建的,JS提供了内置的构造函数Array、Object、Function。这些内置的构造函数有自己的原型对象来提供属性和方法让实例继承。
我们可以自定义构造函数
function Person(name,age,job){
this.name=name;
this.age=age;
this.job=job;
this.sayName=function(){
alert(this.name);
}
}
var person1=new Person('zzh',21,'student');
var person1=new Person('ly',22,'student');
console.log(person1 instanceof Person) //true
和工厂模式的不同:
- 直接将属性和方法赋给this对象
- 没有return语句(若有return语句,并return的是一个对象时,构造函数会返回这个对象)
- 通过new调用函数
用new方法创建Person实例有以下几步
- 创建一个新对象
- 将构造函数的this绑定到新创建的对象上
- 执行构造函数中的代码(为新对象添加属性)
- 返回新对象
构造函数的优缺点
优点:简单、并且能为实例标识为一种特定类型
缺点:创建对象实例时会重复创建一些共享的属性和方法(如上面的sayName方法会每次都创建)
原型模式
针对构造函数模式的缺点,又有一种新的模式出现
像上面的sayName方法在每个对象实例都存在,而构造函数模式在创建对象实例时会重复创建sayName方法。解决的方法可以将sayName方法定义成全局函数,然后在构造函数中引用这个方法即可。
不过又产生了新的问题:定义过多的全局变量可能会造成命名冲突也会占用过多资源(全局变量的作用域和生存期都在整个程序运行过程中)
我们引用了原型的概念
每个函数都有一个原型对象,这个原型对象的作用就是保存需要共享的属性和方法,对象实例通过原型链可以继承原型对象的属性和方法。这样就不用重复去创建那些要共享的属性和方法了。
function Person(){
Person.prototype.name='zzh';
Person.prototype.sayName=function(){
alert(this.name) //this指向调用sayName方法的对象
}
}
var person1=new Person();
var person2=new Person();
//person1和person2都会继承Person的原型
优点:解决了共享属性重复创建的问题
缺点:当原型对象保存引用类型的值时,在一个对象实例中改变这个值会影响所有对象实例中此引用类型的值(因为所有对象实例都是引用的同一内存地址)。
组合模式
很少有单独使用原型模式的。大多数情况都是组合使用构造函数模式和原型模式。
需要共享的属性和方法定义在函数的原型对象上,而对象实例特有的就定义在构造函数中
function Person(name,age,job){
this.name=name;
this.age=age;
this.job=job;
//构造函数中就不要定义原型对象了,因为下面会将原型对象重新指向新对象,从而覆盖这里的定义
}
Person.prototype={
constructor:Person, //因为prototype会指向这个新对象,所以添加这个属性来还原constructor的指向
sayName:function(){
alert(this.name);
}
}