先来讲下最简单的两种建立对象的方式:经过Object构造函数建立以及对象字面量方式。javascript
var person = new Object() person.name = 'youyang' person.age = 18 person.sayName = function () { console.log(this.name) }
var person = { name : 'youyang', age : 18, sayName : function () { console.log(this.name) } }
以上两种方式在建立单个对象时没有问题,但若是建立多个相似对象的话,就会产生大量重复代码,为了解决这一问题,出现了工厂模式建立对象的方法。java
工厂模式是软件工程领域一种广为人知的设计模式,用函数来封装建立一个具体对象的过程,而后就能够屡次调用这个函数以建立类似的对象。设计模式
function createPerson (name, age) { var obj = new Object() obj.name = name obj.age = age obj.sayName = function () { console.log(this.name) } return obj } var p1 = createPerson('youyang', 18) var p2 = createPerson('xiaoqu', 28)
这种方式虽然解决了建立多个类似对象的问题,可是这种方式建立的对象没法判断对象的类型。函数
ECMAScript中的构造函数可用来建立特定类型的对象。所以自定义类型对象能够像下面这样建立:this
function Person (name, age) { this.name = name this.age = age this.sayName = function () { console.log(this.name) } } var p1 = new Person('youyang', 18) var p2 = new Person('youyang', 20)
为了区分构造函数和普通函数,构造函数的首字母要大写,不过构造函数自己也是一个函数,只不过是用来建立对象而已。同时使用构造函数建立实例对象,必须使用new操做符,以这种方式调用构造函数实际上会经历下面4个步骤:.net
console.log(p1 instanceof Person) // true console.log(p2 instanceof Person) // true
另外再次说明,构造函数也是函数,它和其余函数的不一样仅仅在于能够用new来调用建立一个实例对象,当构造函数不用new来调用时那它就和普通函数就没有区别。prototype
构造函数解决了对象的类型检测问题,可是它依然仍是存在缺陷的,观察构造函数建立对象的代码,每建立一个对象,就会执行一次Person构造函数,而在函数内部有一个sayName方法,那么每执行一次Person,就会建立一个sayName方法,也就是p1实例的sayName方法和p2实例的sayName方法不是一个方法。设计
console.log(p1.sayName == p2.sayName) // false
那么当建立多个对象时就会相应的建立多个函数,而一样功能的方法,建立多个是没有必要的。指针
每一个函数都有一个prototype属性,一样每一个构造函数都有一个prototype,这个属性是一个指针,它指向一个对象,这个对象就是原型对象。而原型对象上的属性是能够被全部由该构造函数建立的实例共享的。
理解原型模式的工做原理必须先理解原型对象,建议先看下上篇文章:理解原型对象
下面是原型模式建立对象的例子:code
function Person () {} Person.prototype = { constructor: Person, name : 'youyang', age : 18, colors : ['red', 'yellow', 'blue'] sayName : function () { console.log(this.name) } } var p1 = new Person() var p2 = new Person()
而原型模式的缺点一方面,它没办法像构造函数那样传参的方式动态获取不一样的属性值,全部的对象实例共享了原型上的属性,它们默认取得的是相同的属性值,另外一方面而=原型模式的共享本质致使了更加严重的问题,就是全部实例共享的引用类型属性会相互影响。
p1.colors.push('green') console.log(p2.colors) // ['red', 'yellow', 'blue', 'green']
可见改变了p1实例的colors属性,然而p2实例受到了影响,这是咱们不但愿看到的。
建立自定义类型最多见的方式就是组合使用构造函数模式与原型模式,构造函数模式用于定义实例属性,而原型模式用于定义共享的方法和属性。
function Person (name, age) { this.name = name this.age = age this.colors = ['red', 'yellow', 'blue'] } Person.prototype = { constructor: Person, sayName : function () { console.log(this.name) } } var p1 = new Person('youyang', 18) var p2 = new Person('xiaoqu', 20) p1.colors.push('green') console.log(p1.colors) // ['red', 'yellow', 'blue', 'green'] console.log(p2.colors) // ['red', 'yellow', 'blue'] console.log(p1,sayName === p2.sayName) // true
因而可知,原型模式与构造函数模式组合使用,既能动态的定义每一个实例对象本身的属性,并且引用类型属性互不影响,同时还共享了原型上的方法。
这种构造函数与原型混合的模式是javascript中使用最普遍认同度最高的一种建立自定义类型的方法。
除了上面介绍的集中建立对象的方式,还有动态原型模式,寄生构造函数模式和稳妥构造函数模式,其中动态原型模式是在组合模式(构造函数模式和原型模式组合使用)的基础上再进一步修改得来,将原型的初始化操做也一同封装进构造函数中。寄生构造函数模式和稳妥构造函数模式也都有相应的应用场景,可是不多使用到,就不一一介绍了。