一、如何创建对象

1、new关键字

  1. var obj = new Object()

2、字面量

  1. var obj = {}

二、工厂模式

上面创建对象的方式缺点:使用一个接口创建很多对象,就会产生大量重复代码,于是就有了工厂模式。

  1. function createPerson(name, age, job) {
  2. var o = new Object()
  3. o.name = name
  4. o.age = age
  5. o.job = job
  6. o.sayName = function () {
  7. console.log(this.name)
  8. }
  9. return o
  10. }
  11. var person1 = createPerson('zhangsan', 18, '教师')
  12. var person2 = createPerson('lisi', 20, '司机')

优点:能够根据接受参数不同来构建一个包含所有信息的Person对象,可以无数次的调用,返回一个方法对象。
缺点:返回的对象只能包含已经定义好的属性;没有解决对象识别问题(不知道一个对象的类型)。

三、构造函数模式

  1. function Person(name, age, job) {
  2. this.name = name
  3. this.age = age
  4. this.job = job
  5. this.sayName = function(){
  6. console.log(this.name)
  7. }
  8. }
  9. var person1 = new Person('wangwu', 22, '工程师')
  10. var person2 = new Person('zhangsan', 25, '测试师')
  11. 和工厂模式的不同之处:
  12. 1、没有显示的创建对象
  13. 2、直接将属性和方法赋值给this对象
  14. 3、没有return语句

优点:可以用constructor来标识对象类型
缺点:每个方法都要在每个实例上重新创建一遍。(也就是sayName要被多次创建,浪费内存)
过渡方法:可以将方法写在外面

  1. function Person(name, age, job) {
  2. this.name = name
  3. this.age = age
  4. this.job = job
  5. this.sayName = sayName
  6. }
  7. function sayName(){
  8. console.log(this.name)
  9. }
  10. var person1 = new Person('wangwu', 22, '工程师')
  11. var person2 = new Person('zhangsan', 25, '测试师')

虽然可以解决函数重复创建,但是函数如果很多,全部放在全局作用域下,就起不到封装的作用。

四、原型模式

  1. function Person(){}
  2. Person.prototype.name = 'lisi'
  3. Person.prototype.age = 21
  4. Person.prototype.job = '当兵'
  5. Person.prototype.sayName = function(){
  6. console.log(this.name)
  7. }
  8. var person1 = new Person()
  9. person1.sayName() // lisi
  10. var person2 = new Person()
  11. person2.sayName() // lisi
  12. console.log(person1.sayName == person1.sayName) // true
  13. 与构造函数不同的是,新对象的这些属性和方法都是由所有实例共享的

优点:可以让所有实例共享它所包含的属性和方法。(不必在构造函数中定义对象实例的信息,而是可以将这些信息 直接添加到原型对象中)
缺点:由其共享的本性所导致的。省略了为构造函数传递参数。私有属性可能都会一起改变。

  1. function Person(){}
  2. Person.prototype = {
  3. name: 'lisi',
  4. age: 12,
  5. job: '',
  6. firends: ['1', '2'],
  7. sayName: function(){
  8. console.log(this.name)
  9. }
  10. }
  11. var person1 = new Person()
  12. var person2 = new Person()
  13. person1.firends.push('3')
  14. console.log(person1.firends) // ['1', '2', '3']
  15. console.log(person2.firends) // ['1', '2', '3']

五、构造函数和原型组合模式

  1. function Person(name, age, job){
  2. this.name = name
  3. this.age = age
  4. this.job = job
  5. }
  6. Person.prototype = {
  7. constructor: Person,
  8. sayName: function(){
  9. console.log(this.name)
  10. }
  11. }

优点:构造函数用于定义实例属性,原型模式用于定义方法和共享属性。
缺点:其他OO语言开发人员看到这样写法会有些困惑

六、动态原型模式

  1. function Person(name, age, job){
  2. this.name = name
  3. this.age = age
  4. this.job = job
  5. if(typeof this.sayName != 'function') {
  6. Person.prototype.sayName = function(){
  7. console.log(this.name)
  8. }
  9. }
  10. }
  11. var p1 = new Person('zhangsan', 12, 'wuye')
  12. p1.sayName()

优点:把所有信息都封装在构造函数中,通过构造函数初始化原型,判断原型中是否有sayName方法,如果没有救添加。
缺点:不能使用对象字面量重写原型。(因为如果在已经创建了实例的情况下重写原型,那么就会切断现有实例与新原型之间的联系)

七、寄生构造函数模式

如果前面几种模式都不适用,可以使用寄生构造函数模式

  1. function SpecialArray(){
  2. var values = new Array() // 创建数组
  3. values.push.apply(values, arguments) // 添加值
  4. values.toPipedString = function(){ // 添加方法
  5. return this.join('|')
  6. }
  7. return values // 返回数组
  8. }
  9. var colors = new SpecialArray('red', 'blue', 'green')
  10. console.log(colors.toPipedString()) // 'red|blue|green'

优点:可以创建额外方法
缺点:返回的对象与构造函数或者与构造函数的原型属性之间没有关系。

八、稳妥构造函数模式

所谓稳妥,指的是没有公共属性,而且其方法也不引用this的对象。

  1. function Person(name, age, job) {
  2. var obj = new Object()
  3. // 可以在这里定义私有变量和函数
  4. // 添加方法
  5. obj.sayName = function(){
  6. console.log(name)
  7. }
  8. return obj
  9. }
  10. // 除了sayName方法之外,没有其他途径访问其数据成员

优点:保护数据不被更改
缺点:只能通过对象中的方法,才能访问对象中的属性

九、总结

工厂模式 ——-> 构造函数模式 ——-> 原型模式 ——-> 构造函数和原型组合模式

工厂模式 构造函数模式 原型模式 构造函数和原型组合模式 动态原型模式 寄生构造函数模式 稳妥构造函数模式
优点 能够根据接受参数不同来构建一个包含所有信息的Person对象,可以无数次的调用,返回一个方法对象 可以用constructor来标识对象类型 可以让所有实例共享它所包含的属性和方法。(不必在构造函数中定义对象实例的信息,而是可以将这些信息 直接添加到原型对象中) 构造函数用于定义实例属性,原型模式用于定义方法和共享属性。 把所有信息都封装在构造函数中,通过构造函数初始化原型,判断原型中是否有sayName方法,如果没有救添加。 可以创建额外方法 保护数据不被更改
缺点 返回的对象只能包含已经定义好的属性;没有解决对象识别问题(不知道一个对象的类型) 每个方法都要在每个实例上重新创建一遍。(也就是sayName要被多次创建,浪费内存) 由其共享的本性所导致的。省略了为构造函数传递参数。私有属性可能都会一起改变。 其他OO语言开发人员看到这样写法会有些困惑 不能使用对象字面量重写原型 返回的对象与构造函数或者与构造函数的原型属性之间没有关系。 只能通过对象中的方法,才能访问对象中的属性
过渡 将方法放在全局作用域,这样做不能很好的起到封装的作用
备注