工厂模式,构造器模式和原型模式

1,182 阅读4分钟

一、工厂模式

工厂模式属于创建型模式,它提供了一种创建对象的最佳方式。我们在函数内部创建一个对象,赋予对象属性和方法,并通过return返回这个对象

function createPerson(name, age, job) {
    var o = new Object()
    o.name = name
    o.age = age
    o.job = job
    o.sayName = function() {
      console.log(this.name)
    }
    return o
  }
  var person1 = createPerson('caoyuan', 25, 'Software engineer')
  var person2 = createPerson('neil', 23, 'Software engineer')
  console.log(person1) // {name: "caoyuan", age: 25, job: "Software engineer", sayName: ƒ}
  console.log(person2) // {name: "neil", age: 23, job: "Software engineer", sayName: ƒ}

二、构造函数模式

构造模式和工厂模式创建对象的不同点在于;

  1. 没有显式的创建对象
  2. 直接将属性和方法赋值给了this对象
  3. 没有return对象
function Person(name, age, job) {
    this.name = name
    this.age = age
    this.job = job
    this.sayName = function() {
      console.log(this.name)
    }
  }
  let person1 = new Person('caoyuan', 25, 'Software Engineer')
  let person2 = new Person('neil', 23, 'Software engineer')
  person1.sayName()
  person2.sayName()

以这种方式调用构造函数实际会经历一下四个步骤

  1. 创建一个对象
  2. 将构造函数的作用域赋给新对象(因此this就指向这个对象)
  3. 执行构造函数种的代码(为这个新对象添加属性)
  4. 返回对象

寄生构造函模式

这种模式的基本思想是创建一个函数,该函数的作用仅仅是封装对象的代码,然后再返回新创建的对象。

function Person(name, age, job) {
    var o = new Object()
    o.name = name
    o.age = age
    o.job = job
    o.sayName = function() {
      console.log(this.name)
    }
    return o
  }
  let friend = new Person('neil', 25, 'software Engineer')
  friend.sayName() // neil

以上就是例子,把new操作符抛去,这个模式和工厂模式一模一样。构造函数在不返回值的情况下,默认会返回新对象实例,而通过构造函数的末尾添加一个return语句,可以重写调用给构造函数时返回的值。 如下例子

function SpecialArray () {
    // 创建数组
    var values = new Array()
    // 添加值
    values.push.apply(values, arguments)
    // 添加方法
    values.toPipedString = function() {
      return this.join('|')
    }

    // 返回数组
    return values

  }
  var color = new SpecialArray('1','2','3')
  console.log(color.toPipedString()) // 1|2|3

稳妥构造函数模式 稳妥对象是指没有公共属性, 方法也不引用this的对象。适合在安全的环境中,或者防止数据被其他应该改动时使用

稳妥模式遵循与寄生构造函数类似的模式,但 有两点不同;

  1. 创建新对象的实例不引用this;
  2. 不使用new操作符调用构造函数
function Person(name, age, job) {
    var o = new Object()
    o.sayName = function() {
      console.log(name)
    }
    return o
  }

  let person = Person('neil', 25, 'software Engineer')
  person.sayName() // neil

作用域安全的构造函数

通过代码介绍下作用域安全的构造函数
function Person(name, age, job) {
    this.name = name
    this.age = age
    this.job = job
  }
 var person = Person('neil', 25, 'Softwate Engineer')
 console.log(window.age) // 25

假设用上述这种方式调用,this指向window,那么值也就在window上了,那么怎么才能解决这种问题呢, 修改代码如下

function Person(name, age, job) {
    if (this instanceof Person) {
      this.name = name
      this.age = age
      this.job = job
    } else {
      return new Person(name, age, job)
    }
  }
  var person = Person('neil', 25, 'software Engineer')
  console.log(window.age) // undefined
  console.log(person.age) // 25

通过instanceof来判断当前this是否是person实例上的,现在看上去好像没有什么问题了,那么假设用了构造函数窃取模式的继承且不使用原型链,这个继承可能就被破坏了

function Polygon(sides) {
    if (this instanceof Polygon) {
      this.sides = sides
      this.getArea = function() {
        return 0
      }
    } else {
      return new Polygon(sides)
    }
  }

  function Rectangle(width, heigth) {
    Polygon.call(this, 2) 
    this.width = width
    this.heigth = heigth
    this.getArea = function () {
      return this.width * this.heigth
    }
  }
  let rect = new Rectangle(5, 10)
  console.log(rect.sides) // undefined

你会发现,rect.sides为undefined,为啥呢,Polygon这个实例是安全的,但是,Rectangle中是通过call来继承Polygon的sides属性,这是因为this对象不是Polygon的实例,所以创建了一个新的Polygon对象,Rectangle中并没有sides属性。如果构造函数窃取结合使用原型链和寄生模式就可以解决这个问题了,代码如下

function Polygon(sides) {
    if (this instanceof Polygon) {
      this.sides = sides
      this.getArea = function() {
        return 0
      }
    } else {
      return new Polygon(sides)
    }
  }

  function Rectangle(width, heigth) {
    Polygon.call(this, 2) 
    this.width = width
    this.heigth = heigth
    this.getArea = function () {
      return this.width * this.heigth
    }
  }
  Rectangle.prototype = new Polygon()
  let rect = new Rectangle(5, 10)
  console.log(rect.sides) // 2

三、原型模式

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

  function Person() {
  }
  Person.prototype.name = 'caoyuan'
  Person.prototype.age = '25'
  Person.prototype.sayName = function() {
    console.log(this.name)
  }
  let person1 = new Person()
  let person2 = new Person()
  person2.sayName()
  person1.sayName()
  console.log(person1.sayName == person2.sayName)

动态原型模式

它把所有信息都封装在了构造函数中,而通过在构造函数中初始化原型,也保持了同时使用构造函数和原型的优点

function Person(name, age, job) {
    this.name = name
    this.age = age
    this.job = job
    // 方法
    if (typeof this.sayName != 'function') {
      Person.prototype.sayName = function() {
        console.log(this.name)
      }
    }
  }
  let person = new Person('neil', 25, 'Software Engineer')
  person.sayName()

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

创建自定义对象最常见的方式,就是组合使用构造函数模式和原型模式。

  1. 构造函数模式定义实例的属性
  2. 原型模式用于定义方法和共享的属性
function Person(name, age, job) {
  this.name = name
  this.age = age
  this.children = ['1', '2']
}
Person.prototype.sayName = function() {
  console.log(this.name)
}

let person1 = new Person('caoyuan', 25, 'Software Engineer')
let person2 = new Person('neil', 23, 'Software Engineer')

person1.children.push('3')
console.log(person1.children) // Array [1,2,3]
console.log(person2.children) // Array [1,2]
console.log(person1.children === person2.children) // false
console.log(person1.sayName === person2.sayName) // true

相关文章:

原型链基础及多种使用方式