JavaScript——创建对象的模式(一)
最近了解了几种创建对象的模式,下面总结一下它们的区别。
- 工厂模式
- 构造函数模式
- 原型模式
- 组合构造函数模式和原型模式
- 动态原型模式
- 寄生构造函数模式
- 稳妥构造函数模式
工厂模式
用函数封装以特定接口创建对象的细节。
function a (name) {
var obj = new Object();
obj.name = name;
obj.hello = function () {
alert("hello "+this.name);
};
return obj;
}
var a1 = a("one");
var a2 = a("two");
工厂模式解决了创建多个相似对象的问题,但是无法知道对象的类型。
构造函数模式
构造函数可以创建指定类型的对象。创建自定义的构造函数可以创建自定义对象的属性和方法。
function A (name) {
this.name = name;
this.hello = function () {
alert("hello "+this.name);
};
}
var a1 = new A("one");
var a2 = new A("two");
这里遵循构造函数命名规则第一个字母要大写,与工厂模式相比,构造函数模式没有显式创建对象,直接把属性和方法赋给this对象,同时也没有返回对象,在创建A对象时要使用new,这种方式的过程:1.创建一个新对象 2. 将构造函数的作用域赋给新对象(this指向这个对象)3.执行构造函数(新对象添加属性方法)4.返回对象。
a1和a2分别保存着A的一个不同的实例,两个对象里都有一个constructor(构造函数)属性指向A
alert(a1.contructor == A);//true
alert(a2.contructor == A);//true
检测对象类型最后还是用instanceof,a1、a2也是Object的实例。
alert(a1 instanceof A);//true
alert(a1 instanceof Object);//true
以这种方式定义的构造函数是定义在Global对象(在浏览器是window对象)的。
1.将构造函数当作函数
构造函数与其他函数的区别在于调用方式不同,任何函数,只要通过new调用就可以作为构造函数,不通过new调用就跟普通函数一样
//当作构造函数
var a = new A("one");
a.hello();//“hello one”
//普通函数
A("one");//添加到window
window.hello();//"hello one"
//在另一个对象的作用域中调用
var obj = new Object();
A.call(obj,"one");
obj.hello();//"hello one"
2.构造函数的问题
构造函数的问题就是每个方法都要在每个实例上重新创建一遍,a1、a2都有hello方法,但是两个方法不是同一个实例。函数也是对象,实例化函数也是实例化对象。也就是说a1和a2的hello方法并不相等。创建两个相同任务的function实例没有必要,而且有this对象,并不用在执行代码前就把函数绑定在特定对象上。
function A (name) {
this.name = name;
this.hello = hello;
}
function hello () {
alert("hello "+this.name);
}
var a1 = new A("one");
var a2 = new A("two");
这样a1、a2 对象共享了在全局作用域定义的同一个hello()函数,解决了两个函数做同一件事的问题,但是这个全局函数只被某个对象调用,不符合全局的说法。而且如果方法很多,就要定义很多全局函数,那我们自定义的类型就没有封装性了。
原型模式
每个函数都有一个prototype属性,是一个指向对象的指针,使用这个属性我们就不必在构造函数中定义对象的实例信息,可以将这些信息直接添加到原型对象中。
function A(){
}
A.prototype.name = "张三";
A.prototype.hello = function(){
alert("hello " + this.name);
}
var a1 = new A();
a1.hello(); //"张三"
var a2= new A();
a2.hello(); //"张三"
alert(a1.hello == a2.hello);//true
属性和方法都添加在原型上,构造函数实为空函数,与纯构造函数创建的对象不同,这些属性和方式都是公用的(单例模式)这个是优点也是缺点,函数和基本值属性共享很好,但是如果是引用属性就有问题了。
function A(){}
A.prototype = {
constructor:A,
name:"张三",
cwu:["cat","dog"],
hello:function(){
alert("hello " + this.name);
}
}
var a1= new A();
var a2= new A();
a1.cwu.push("fish");
console.log(a1.cwu); //["cat","dog","fish"]
console.log(a2.cwu); //["cat","dog","fish"]
console.log(a1.cwu=== a2.cwu); //true
因为共享,改变其中一个的实例就会改变全部的实例。
组合构造函数模式和原型模式
结合两者优点,这种模式也是比较常用的。既存在公共属性和方法,又可以自带私有参数
function A(name){
this.name = name;
this.cwu = ["cat","dog"];
}
A.prototype = {
constructor:A,
hello:function(){
alert("hello " + this.name);
}
}
var a1= new A("张三");
var a2= new A("哈哈");
a1.cwu.push("fish");
console.log(a1.cwu); //["cat","dog","fish"]
console.log(a2.cwu); //["cat","dog"]
console.log(a1.cwu=== a2.cwu); //false
console.log(a1.hello === a2.hello); //true