工厂函数和构造函数

avatar
前端工程师 @豌豆公主

工厂函数(factory function)和构造函数(constructor function)

JS基于面向对象的创建对象,主要有工厂模式、构造函数模式、原型模式三种。本文主要分析了这前两种模式的特点、利弊,以及一些细节问题。

工厂函数

我们使用对象字面量的形式,可以声明一个对象,有键,有值。当我们需要声明很多一样的键,一样的值,我们需要重复写很多相同的代码

image.png

此时就可以使用工厂函数,外部不关心内部是怎么实现的,只需要传入正确的参数,就可以声明对象

image.png

是不是很方便,只需要传正确的参数,就可以开心的创建对象了。
工厂函数,主要解决了代码冗余,封装成了函数,统一处理。
总共有三步:1. 创建对象 2. 对象赋值 3. 返回对象

  工厂函数优缺点

  • 优点:只需要传入一个正确的参数,就可以获取你所需要的对象,而无需知道其创建细节。

  • 缺点:

    1.工厂函数的职责相对过重,增加新的feature,需要修改工厂类的判断逻辑,违背了开闭原则

    2.创建的对象与工厂函数的prototype之间没有(只是在用工厂函数,实际跟工厂函数没有原型上关联)

    • 开闭原则

    当程序需要变化时,尽量通过扩展程序来实现修改。而不是修改现有代码来实现变化

    • 与prototype无联系

image.png

二、构造函数模式

我们将上面的例子进行改造

image.png

可以观察到,构造函数,使用了this,并且使用了new 来实例化对象。 并没有像工厂函数,声明对象,返回对象。因为new,为我们做了这些操作

  构造函数优缺点

  • 优点:创建自定义函数,可以通过prototype与 构造函数做关联,这是构造函数和工厂函数最大的不同
  • 缺点:
  1. 继承的限制:在 JavaScript 中,基于原型链的继承可能会导致构造函数模式存在一些继承上的限制,因为它并不是完全基于类的面向对象模式。ES6 的类引入了更好的继承机制。
  2. 过度使用可能导致复杂性:如果过度依赖构造函数模式,可能导致类的设计变得复杂,不利于维护和拓展。

因为做的事情是一样的,所以没必要定义俩个不同的 Function 实例,我们可以把函数定义转移到构造函数外部。 转移到外部,这样虽然解决了相同逻辑的函数重复定义的问题,但全局作用域也因此被搞乱了,因为那个函数实际上只能在一个对象上调用。如果这个对象需要多个方法,那么就要在全局作用域中定义多个函数。这会导致自定义类型引用的代码不能很好地聚集一起。这个新问题可以通过原型模式来解决

工厂函数和构造函数的总结

  1. 从广义上讲,我们所有的函数封装(除了构造函数),都是工厂函数

     工厂函数是将返回一个新的object的任何不是类或者构造函数的函数。在js中,任何函数都能够返回一个object.如果我们不是通过new function()的方式来获得这个对象的,那么她就是一个factory工厂函数
     
    
  2. 工厂函数,只是一个躯壳,只负责加工生产。不产生原型上的联系(这也是和构造函数最大的不同)

  3. 工厂函数和构造函数,都是单一的实现。如果要新增feature,那么需要再原来的逻辑上进行修改,不满足设计模式的开闭原则

  4. prototype是二者最大的区别,这个区别,引申出了构造函数的内部实现,比如this,new

  5. 工厂函数不会有欺骗性的 instanceof

         如果构造函数的原型发生变化,instanceOf将不再准确
    
  6. 构造函数要使用new, 如果不是用new,this会直接指向window,各个属性会直接赋值到window上。如果window上没有相关属性,会直接undefined.可以做一个作用域安全的构造函数


// 作用域安全的构造函数
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);
  }
}

这也类似于Symbol 对new 的拦截

image.png

以上就是对工厂函数和构造函数的梳理啦~