JavaScript 设计模式:模块模式和揭示模块模式之间的区别

JavaScript design pattern: difference between module pattern and revealing module pattern?

本文关键字:模块 模式 之间 区别 设计模式 JavaScript      更新时间:2023-09-26

我最近在读《学习JavaScript设计模式》一书。我不明白的是模块模式和揭示模块模式之间的区别。我觉得它们是一回事。任何人都可以举个例子吗?

至少有三种不同的方法可以实现模块模式,但揭示模块模式是唯一具有正式名称的模块模式后代。

基本模块模式

模块模式必须满足以下条件:

  • 私人成员住在封闭区。
  • 公共成员在返回对象中公开。

但是这个定义有很多歧义。通过以不同的方式解决歧义,您可以获得模块模式的变体。

揭示模块模式

揭示模块模式

是最著名和最受欢迎的模块模式变体。与其他替代方案相比,它具有许多优点,例如

  • 在不更改函数体的情况下重命名公共函数。
  • 通过修改单行将成员从公共更改为私有,反之亦然,而无需更改函数体。

RMP除了原始条件外,还满足三个附加条件:

  • 所有成员(无论是公共成员还是私有成员)都在闭包中定义。
  • 返回对象是没有函数定义的对象文本。所有右侧表达式都是闭包变量
  • 所有引用都是通过闭包变量,而不是返回对象。

以下示例显示了它的使用方式

var welcomeModule = (function(){
  var name = "John";
  var hello = function(){ console.log("Hello, " + name + "!");}
  var welcome = function() { console.log( hello() + " Welcome to StackOverflow!");}
  return {
    name: name,
    sayHello: hello,
    sayWelcome: welcome
  }
})();

如果要将namesayHello设为私有,则只需注释掉返回对象中的相应行即可。

var welcomeModule = (function(){
  var name = "John";
  var hello = function(){ console.log("Hello, " + name + "!");}
  var welcome = function() { console.log( hello() + " Welcome to StackOverflow!");}
  return {
    //name: name,
    //sayHello: hello,
    sayWelcome: welcome
  }
})();

具有对象文本的模块模式

这可能是模块模式最古老的变体。与RMP不同,这个变体没有性感的官方名称。

除原件外,它还满足以下条件:

  • 私有成员在闭包中定义。
  • 公共成员在返回对象文本中定义。
  • 对公共成员的引用尽可能通过this

在下面的示例中,您可以看到与 RMP 相比,函数定义实际上是在返回对象文本中,并且对成员的引用由 this 限定。

var welcomeModule = (function(){
  return {
    name: "John",
    sayHello: function(){ console.log("Hello, " + this.name + "!");}
    sayWelcome: function() { console.log( this.hello() + " Welcome to StackOverflow!");}
  }
})();

请注意,与 RMP 不同,为了使namesayHello私有,还必须更改在各种函数体定义中指向namesayHello的引用。

var welcomeModule = (function(){
  var name = "John";
  var sayHello = function(){ console.log("Hello, " + name + "!");};
  return {
    //name: "John",
    //sayHello: function(){ console.log("Hello, " + this.name + "!");}
    sayWelcome: function() { console.log( hello() + " Welcome to StackOverflow!");}
  }
})();

带有返回对象存根的模块模式

这个变体也没有正式名称。

除原件外,它还满足以下条件:

  • 开头定义一个空的返回对象存根。
  • 私有成员在闭包中定义。
  • 公共成员定义为存根的成员
  • 对公共成员的引用是通过存根对象

使用我们的旧示例,您可以看到公共成员直接添加到存根对象中。

var welcomeModule = (function(){
  var stub = {};
  stub.name = "John";
  stub.sayHello = function(){ console.log("Hello, " + stub.name + "!");}
  stub.sayWelcome = function() { console.log( stub.hello() + " Welcome to StackOverflow!");}
  return stub;
})();

如果要像以前一样使namesayHello私有,则必须更改对现在私有成员的引用。

var welcomeModule = (function(){
  var stub = {};
  var name = "John";
  var sayHello = function(){ console.log("Hello, " + name + "!");}
  
  stub.sayWelcome = function() { console.log( hello() + " Welcome to StackOverflow!");}
  return stub;
})();

总结

揭示模块模式

与模块模式的其他变体之间的区别主要在于如何引用公共成员。因此,RMP更易于使用和修改,这是其受欢迎程度的原因。然而,这些优势是要付出巨大代价的(在我看来),Addy Osmani 在他关于揭示模块模式的帖子中提到了这一点,

此模式的缺点是,如果私有函数引用公共函数,则在需要修补程序时无法覆盖该公共函数。这是因为私有函数将继续引用私有实现,并且该模式不适用于公共成员,仅适用于函数。

引用私有变量的公共对象成员也受上述无补丁规则注释的约束。

因此,使用揭示模块模式

创建的模块可能比使用原始模块模式创建的模块更脆弱,因此在使用过程中应小心。

我在其他一些帖子中谈到过。

简短的回答,在模块模式中,我们在返回对象中定义函数。

在揭示模块模式中,我们在闭包区域中定义函数,并且仅在返回对象中使用变量名。

这样做可以简化代码并具有许多其他优点

只需尝试根据顶部评论答案做一些事情。如果通过将其传递给私有函数来调用私有函数,可能会更安全,因此私有函数可以使用变量引用公共函数。

我使用以下代码创建了一个揭示模块模式。

var HTMLChanger = (function () {
    var privateFunc = function () {
        this.sayHello();
    }
    var hello = function () {
        console.log('say Hello');
    }
    var callPrivate = function () {
        privateFunc.call(this);
    }

    return {
        sayHello: hello,
        callPrivate: callPrivate
    };
})();
HTMLChanger.callPrivate();
//say Hello
HTMLChanger.sayHello = function() { console.log('say Hi!') };
HTMLChanger.callPrivate();
//say Hi!

如您所见,我们可以覆盖公共成员。

模块设计模式和揭示模块设计模式都是构建 JavaScript 代码以封装和组织功能的方法。它们都旨在实现更好的模块化和封装,使代码更易于管理、理解和维护。但是,它们在如何公开和组织其方法和属性方面存在一些差异。

模块设计模式:模块设计模式涉及创建一个匿名函数,该函数将私有数据和方法封装在闭包中。这将创建一个私有作用域,其中变量和函数在全局作用域中隐藏。此模式通常返回一个对象,其中包含可从外部访问的方法和属性,从而有效地充当模块的公共 API。下面是一个基本示例:

const Module = (function() {
  // Private variables and functions
  let privateVar = 10;
  function privateFunction() {
    console.log("Private function called");
  }
  // Public API
  return {
    publicVar: 20,
    publicFunction: function() {
      console.log("Public function called");
    }
  };
})();
console.log(Module.publicVar); // 20
Module.publicFunction(); // "Public function called"

揭示模块设计模式:揭示模块设计模式是模块设计模式的变体。它不是返回包含公共 API 的对象,而是将私有函数和属性映射到闭包中的公共 API,从而有效地仅显示要公开的部分。这可以通过更明确地显示公共方法的结构,使代码更简洁、更自我记录。下面是一个示例:

const RevealingModule = (function() {
  let privateVar = 10;
  function privateFunction() {
    console.log("Private function called");
  }
  function publicFunction() {
    console.log("Public function called");
  }
  // Reveal only the public parts
  return {
    publicVar: privateVar,
    publicFunction: publicFunction
  };
})();
console.log(RevealingModule.publicVar); // 10
RevealingModule.publicFunction(); // "Public function called"

总之,虽然这两种模式共享封装和模块组织的概念,但揭示模块设计模式更加强调通过显式列出公共函数和变量来清楚地揭示公共 API,从而更容易看到哪些部分应该在外部使用。这两种模式之间的选择取决于您的首选项和项目的特定需求。