1. 对象字面量方式
对象字面量方式是建立自定义对象的首选模式,简单方便。php
var per = { name:'zhangsan', age:25, job:'html', sayName:function(){ alert(this.name); } }
**缺点:**使用同一个接口建立不少对象,会产生大量的重复代码。好比我想再建立一个per1对象,我就得把上面的代码再从新写一遍,改变不一样的属性值。html
**二、工厂模式**
工厂模式抽象了建立具体对象的过程。因为在ECMAScript中没法建立类,开发人员就发明了一种函数,用函数来封装以特定接口建立对象的细节,以下面的例子:前端
function createPerson(name,age,job){ var o = new Object(); o.name = name; o.age = age; o.job = job; o.sayName = function(){ alert(this.name); } return o; } var person1 = createPerson('zhang',30,'java'); var person2 = createPerson('zhao',25,'php'); //前端全栈学习交流圈:866109386 //面向1-3经验年前端开发人员 //帮助突破技术瓶颈,提高思惟能力
函数createPerson()可以根据接受到的参数来构建一个包含全部必要信息的Person对象。能够无数次的调用这个函数,而每次它都会返回一个包含三个属性和一个方法的对象。html5
缺点:工厂模式虽然解决了建立多个类似对象的问题,但却没有解决对象识别的问题(即怎样知道一个对象的类型)。java
**三、构造函数模式**
可使用构造函数模式将前面的例子重写以下:数组
function Person(name,age,job){ this.name= name; this.age = age; this.job = job; this.sayName = function(){ alert(this.name); } } var person1 = new Person('zhang',30,'java'); var person2 = new Person('zhao',25,'php'); //前端全栈学习交流圈:866109386 //面向1-3经验年前端开发人员 //帮助突破技术瓶颈,提高思惟能力
建立自定义的构造函数意味着未来能够将它的实例标识为一种特定的类型。而这正是构造函数模式赛过工厂模式的地方。函数
然而,使用构造函数的主要问题,就是每一个方法都要在每一个实例上从新建立一遍。在上面的例子中,person1和person2都有一个名为sayName()的方法,但那两个方法不是同一个Function的实例,建立两个完成一样任务的Function实例的确没有必要;何况有this对象在,根本不用在执行代码前就把函数绑定到特定对象上面。所以能够像下面这样,经过把函数定义转移到构造函数外部来解决这个问题。学习
function Person(name,age,job){ this.name= name; this.age = age; this.job = job; this.sayName = sayName(); } function sayName(){ alert(this.name); } var person1 = createPerson('zhang',30,'java'); var person2 = createPerson('zhao',25,'php');
在这个例子中,咱们把sayName()函数的定义转移到构造函数外部。而在构造函数内部,咱们将sayName属性设置成等于全局的sayName函数,这样person1和person2对象就共享了在全局做用域中定义的同一个sayName()函数。这样确实解决了两个函数在作同一件事的问题,但是新问题又来了:在全局做用域中定义的函数实际上只能被某个对象调用,这让全局做用域有点名存实亡。更让人没法接受的是:若是对象须要定义不少方法,那么就要定义不少全局函数。好在,这些问题能够经过使用原型模式来解决。this
**四、原型模式**
咱们建立的每一个函数都有一个prototype(原型)属性,这个属性是一个指针,指向一个对象,而这个对象的用途是包含能够由特定类型的全部实例共享的属性和方法。使用原型对象的好处是可让全部的对象实例共享他所包含的属性和方法。prototype
function Person(){} Person.prototype.name = 'zhang'; Person.prototype.age = '22'; Person.prototype.job = 'html5'; Person.prototype.sayName = function(){ alert(this.name); }; var person1 = new Person(); var person2 = new Person();
原型模式也不是没有缺点。首先,它省略了为构造函数传递初始化参数这一环节,结果全部实例在默认状况下都将取得相同的属性值。虽然这会在某种程度上带来一些不方便,但还不是原型的最大问题。原型模式的最大问题是由共享的本性所致使的。
原型中全部属性是被不少实例共享的,这种共享对于函数很是合适。对于那些包含基本值的属性倒也说的过去,经过在实例上添加一个同名属性,能够隐藏原型中的对应属性。而后,对于包含引用类型的属性来讲,问题就比较突出了。
function Person(){} Person.prototype = { constructor:Person, name:'zhang', age :'22', job :'html5', friends:\['wang','li'\], sayName : function(){ alert(this.name); } }; var person1 = new Person(); var person2 = new Person(); person1.friends.push('zhao'); alert(person1.friends); //'wang,li,zhao' alert(person2.friends); //'wang,li,zhao' alert(person1.friends === person2.friends); //true
从上面的打印的结果咱们就能够知道为何不多人单独使用原型模式了,实例通常都是要有属于本身的所有属性的。
**五、组合使用构造函数模式和原型模式**
组合使用构造函数模式和原型模式,是建立自定义类型的最多见方式。构造函数模式用于定义实例属性,而原型模式用于定义方法和共享的属性。结果,每一个实例都会有本身的一份实例属性的副本,但同时又共享着对方法的引用,最大限度的节省了内存。
function Person(name,age,job){ this.name= name; this.age = age; this.job = job; this.friends = \['wang','li'\]; } Person.prototype = { constructor:Person, sayName : function(){ alert(this.name); } } var person1 = new Person('zhang',26,'java',); var person2 = new Person('sun',25,'php'); person1.friends.push('zhao'); alert(person1.friends); //'wang,li,zhao' alert(person2.friends); //'wang,li' alert(person1.friends === person2.friends); //false
在上面的例子中,实例属性都是在构造函数中定义的,而由全部实例共享的属性constructor和方法satName()则是在原型中定义的。而修改了person1.friends(向其中添加一个新字符串),并不会影响到person2.friends,由于他们分别引用了不一样的数组。