虽然Object构造函数或对象字面量都可以用来创建单个对象,但是这些方法有明显的缺点:使用同一个接口创建很多对象,会产生大量重复代码。因此人们开始使用工厂模式。

工厂模式

这种模式抽象了创建具体对象的过程

function createPerson(name,age){
    var o = new Object();
    o.name = name;
    o.age = age;
    o.sayName = function(){
        alert(this.name);
    };
    return o;
}

var person1 = new createPerson("liaojin",18);
var person2 = new createPerson("xiaoguan",20);

函数createPerson()能够根据接受的参数来构建一个包含所有必要信息的Person对象。可以无数次的调用这个函数,而每次它都会返回一个包含两个属性以个方法的对象。工厂模式虽然解决了创建多个相似对象的问题,但却没有解决对象识别的问题(即怎样知道一个对象的类型)。

构造函数模式

使用构造函数将上述例子重写

function Person(name,age){
    this.name = name;
    this.age = age;
    this.sayName = function(){
        alert(this.name);
    };
}

var person1 = new Person("liaojin",18);
var person2 = new Person("xiaoguan",20);

在这个例子中,Person()函数取代了createPerson()函数。注意到Person()中的代码与createPerson()的不同之处:
1.没有显示的创建对象
2.直接将属性和方法赋值给了this对象
3.没有return语句
要创建Person新实例,必须使用new操作符。这种方式会经理以下四个步骤:
1.创建一个新对象
2.将构造函数的作用域赋给新对象(因此this就指向了这个新对象)
3.执行构造函数中的代码(为这个新对象添加属性)
4.返回新对象
person1和person2分别保存着Person的一个不同实例。这两个对象都有一个constructor属性,该属性指向Person。

alert(person1.constructor == Person);//true
alert(person2.constructor == Person);//true

对象的constructor属性最初是用来表示对象类型的,但是检测对象类型还是instanceof更可靠一些。这个例子中创建的所有对象即是Object的实例,同时也是Person的实例。
创建自定义的构造函数意味着将来可以将它的实例标识为一种特定的类型;person1和person2之所以同时是Object的实例,是因为所有对象均继承自Object.

将构造函数当做函数

构造函数与其他函数唯一的区别就在于调用他们的方式不同。
任何函数只要通过new操作符来调用,那他就可以作为构造函数;而任何函数如果不通过new操作符来调用,那他跟普通函数没有区别。
如上述例子中的Person()函数可以通过下列任何一种方式来调用。

//当做构造函数调用
var person = new Person("liaojin",18);
person.sayName();//liaojin

//作为普通函数调用
Person("lihua",12);
window.sayName();//lihua

//在另一个对象的作用域调用
var o = new Object();
Person.call(o,"xiaoguan",20);
o.sayName();//xiaoguan
构造函数的问题

构造函数模式虽然好用,但是也有缺点。使用构造函数的主要问题,就是每个方法都要在每个实例上重新创建一遍。如同this.sayName =new function(){alert(this.name);};在上面的构造函数中sayName()的方法,person1和person2虽然都调用了这个方法,但是调用的并不是同一个Function实例。因此不同实例的同名函数是不相等的

alert(person1.sayName == person2.sayName);//false

然而创建两个完成同样任务的Function实例的确没有必要;因此可以通过吧函数定义转移到构造函数外部来解决这个问题

function Person(name,age){
    this.name = name;
    this.age = age;
    this.sayName = sayName;
}
function sayName(){
    alert(this.name);
}
var person1 = new Person("liaojin",18);
var person2 = new Person("xiaoguan",20);

在构造函数内部,我们将sayName属性设置为指向全局的sayName函数,由于sayName包含的是指向函数的指针,person1,person2共享了一个sayName函数,解决了两个函数做同样一件事的问题。
可是随即有产生了新的问题:在全局作用域定义的函数实际上只能被某个对象调用,这让全局函数优点名不副实。如果对象需要定义很多方法,那么就需要定义多个全局函数,于是自定义的引用类型就没有封装性可言了,因此产生了原型模式。

原型模式

我们所创建的每个函数都有一个prototype属性,这个属性是一个指针,指向一个对象,而这个对象的用途就是包含可以由特定类型的所有实例共享的属性和方法prototype就是通过调用构造函数而创建的那个对象实例的原型对象。可以让所有对象的实例共享它所包含的属性和方法。

function Person(){}
Person.prototype.name = "liaojin";
Person.prototype.age = 18;
Person.prototype.sayName = function(){
    alert(this.name);
};

var person1 = new Person();
Person1.sayName();//liaojin
var person2 = new Person();
Person2.sayName();//liaojin

alert(person1.sayName == person2.sayName);//true

组合使用构造函数模式和原型模式

构造函数用于定义实例属性,而原型模式用于定义方法和共享的属性。这样每个实例都会有自己的实例属性的副本,但同时又共享着对方法的引用,最大限度地节省了内存。

function Person(name,age){
    this.name = name;
    this.age = age;
    this.friends = ["lucy","lily"];
}

Person.prototype = {
    constructor:Person,
    sayName:function(){
        alert(this.name);
    }
}
var person1 = new Person("liaojin",18);
var person2 = new Person("xiaoguan",20);

person1.friends.push("lihua");
alert(person1.friends);//lucy,lily,lihua
alert(person2.friends);//lucy,lily
alert(person1.friends == person2.friends);//false
alert(person1.sayName == person2.sayName);//true

动态原型模式

有其他面向对象经验开发的人员看到独立的构造函数和原型时,很可能会肥肠困惑,动态原型模式就是解决这个问题的一个方案。

function Person(name,age){
    this.name = name;
    this.age = age;
}
if(typeof this.sayName != "function"){
    Person.prototype.sayName = function(){
        alert(this.name);
    };
}

这里只在sayName()方法不存在的情况下才会将它添加到原型中。这段代码只会在初期调用才会执行。

寄生构造函数模式

这个模式可以在特殊的情况下用来为对象创建构造函数。假设我们想创建一个具有额外方法的特殊数组。由于不能直接修改Array构造函数,因此可以使用这个模式。

function SpecialArray(){
    var values = new Array();
    values.push.apply(values,arguments);
    values.toPipedString = function(){
        return this.join("|");
    };
    return values;
}
var colors = new SpecialArray["red","blue","green"];
alert(colors.toPipedString());//red|blue|green

说明:关于寄生构造函数模式,首先返回的对象与构造函数或者与构造函数的原型属性之间没有关系;构造函数返回的对象与在构造函数外部创建的对象没有什么不同。因此不能依赖instanceof操作符来确定对象的类型。

稳妥构造函数模式

所谓稳妥对象,指的是没有公共属性,而且其方法也不引用this的对象。稳妥对象最适合在一些安全的环境中(禁止使用this和new的环境),或者在防止数据被其他应用程序改动时使用。与寄生构造函数类似的模式;但有两点不同:1.新创建对象的实例方法不引用this;2.不使用new操作符调用构造函数;

function Person(name,age){
    var o = new Object();
    o.sayName = function(){
    alert(name);
    };
    return o;
}

//使用
var friend = Person("liaojin",18);
friend.sayName();//liaojin

这样变量person中保存的是一个稳妥对象,而除了调用sayName()方法外,没有别的方法可以访问其数据成员,即使有其他代码会给这个对象添加方法或数据成员,但也不可能有别的办法访问传入到构造函数中的原始数据,非常适合在某些安全执行环境下使用。
与寄生构造函数模式类似,使用稳妥构造函数模式创建的对象与构造函数之间也没有什么关系,因此instanceof操作符对这种对象也没有什么意义


liaojin1
170 声望2 粉丝

菜啊