虽然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对象。能够无数次的调用这个函数,而每次它都会返回一个包含两个属性以个方法的对象。工厂模式虽然解决了建立多个类似对象的问题,但却没有解决对象识别的问题(即怎样知道一个对象的类型)。app
使用构造函数将上述例子重写函数
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。this
alert(person1.constructor == Person);//true alert(person2.constructor == Person);//true
对象的constructor属性最初是用来表示对象类型的,可是检测对象类型仍是instanceof更可靠一些。这个例子中建立的全部对象便是Object的实例,同时也是Person的实例。
建立自定义的构造函数意味着未来能够将它的实例标识为一种特定的类型;person1和person2之因此同时是Object的实例,是由于全部对象均继承自Object.prototype
将构造函数当作函数
构造函数与其余函数惟一的区别就在于调用他们的方式不一样。
任何函数只要经过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实例。所以不一样实例的同名函数是不相等的code
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操做符对这种对象也没有什么意义