如何在JavaScript中使用揭示模块模式

78

我偶然发现了这篇文章:JavaScript 的揭示模式。我想在我的项目中使用它。

假设我有一个名为 abc 的函数,并且我在我的主 JavaScript 文件中调用该函数。

这种模式会使事情变得不同吗?有人可以展示一下这种模式的基本示例吗?


3
对我来说,这篇文章是迄今为止我看过的最好的解释。文章链接如下:http://weblogs.asp.net/dwahlin/archive/2011/08/02/techniques-strategies-and-patterns-for-structuring-javascript-code-revealing-module-pattern.aspx - Mohammad Sepahvand
2
为什么要使用揭示模块模式,而不是使用明确模块模式?https://github.com/tfmontague/definitive-module-pattern - tim-montague
10个回答

99

一个小例子:

var revealed = function(){
   var a = [1,2,3];
   function abc(){
     return (a[0]*a[1])+a[2];
   }

   return {
      name: 'revealed',
      abcfn: abc
   }
}();
在初始化匿名函数中,用来给 revealed 赋值的 aabc 是该函数私有的。该函数返回一个包含 name 属性和 abcfn 属性(一个指向 abc function 的引用)的对象字面量。abc function 使用了私有变量 a。这都得益于使用了闭包(同一函数作用域内的所有内容都可以互相引用)。

使用 Revealed:

alert(revealed.name);    //=> 'revealed'
alert(revealed.abcfn()); //=> 5 (1*2+3)

8
这种模式改变了我的生活! - klewis
6
我知道这是老问题了,但是abc()的返回值缺少一个括号 =] - Fillip Peyton
为什么要使用揭示模块模式,而不是使用明确模块模式?https://github.com/tfmontague/definitive-module-pattern - tim-montague
如何创建一个新的Revealed实例? - Neon Warge
@NeonWarge:你不需要这样做。这个函数不是一个构造函数。它返回一个对象字面量,其中aabc被封闭在内。 - KooiInc
@NeonWarge:虽然您可能不再有这个问题,但是如果您删除最后的(),那么您可以随意调用'revealed'多次以创建此类型的新对象。 - JustGage

23

DC = Douglas Crockford
RMP = 模块模式

DC与RMP的区别在于组织和可读性

文章中已经列出了例子。您具体想问什么?因为这些与文件无关,而与闭包相关。

将所有内容放入一个闭包(函数)中,仅公开您希望被访问的部分。 DC风格和RMP之间的区别在于,在前者中,函数在不同位置定义,而在RMP中,它们始终在相同位置定义,然后在公共对象字面上显示

因此,在DC和RMP中,您都有:

  • 闭包使得定义私有部分(变量和函数)成为可能
  • 私有部分
  • 定义公共可见功能和变量(状态)的公共结果

这两种模式仅在可读性方面存在差异。在DC情况下,您不总是知道某个功能将在何处定义,但在RMP中,您总是知道一切都在私有部分中。


1
DC和RMP并不相同--在继承方面,这两者非常不同。例如,请参见https://dev59.com/fGEh5IYBdhLWcg3w3muK#21932064 - I-Lin Kuo

13

6
作者所称的“Douglas Crockford创建对象的模式”实际上是由Richard Cornford等人开发的模块模式。详情请参见此处
关于示例,有很多。请阅读以下文章并查看其中的一些链接:此处

我重新编辑了我的帖子,请告诉我是否还有任何问题 :) - alex
有趣的是,这些东西随着时间的推移不断演变。"揭示"版本已经被 MyLibrary 的David Mark使用了一段时间,我对"非揭示"版本没有任何意见,因此我使用它。 :-) - RobG

1
我喜欢使用揭示模块模式单例模式的混合,这样我就可以保持结构化的代码,并享受模块模式的好处:
var MyFunction = function(){

    var _ = {
       Init: function(){
          _.Config.foo = "hello world";
       },
       Config:{
          foo:null
       },
       ShowAlert:function(){
          alert(_.Config.foo);
       }
    }

    return {
        Init: _.Init,
        ShowAlert: _.ShowAlert
    };
}();

MyFunction.Init();
MyFunction.ShowAlert();

我在我的博客上写了更多关于这个的信息:

http://curtistimson.co.uk/js/mixing-revealing-module-and-singleton-javascript-patterns/


1

补充一下:使用这种模式时,最好将全局依赖项作为参数传递,以使它们明确。你不必这样做,但这使得你的模块需要什么一眼就能看出来。例如:

var myModule = (function ($, loadModule) {
  "use strict";
})(jQuery, load);

在这个例子中,你可以立即在第一行看到你的模块使用了jQuery和另一个负责加载功能的模块。

0

这里是揭示模块模式的小例子。

它提供了一个类似于类的声明私有和公共函数的功能。这是这种模式的主要优点。如果我们不想将某些功能从全局范围内公开访问,则将其设置为私有,其余部分则设置为公共。以下是如何创建私有和公共函数的示例。还有一件事,它是一个自执行的代码块。

  var Calculator = (function () {

        var num1 = 10;
        var num2=5
        var _abc = function () {
            return num1 - num2;
        };

        var _mulFunc = function () {
            return num1 * num2;
        };

        var _divFunc = function () {
            return num1/num2;
        };

        return {
           //public scope
            abc: _abc,
            mulFunc:_mulFunc
         };

    })();

警告(Calculator.abc()); 返回5

警告(Calculator.mulFunc()); 返回50

而__divFunc()将无法访问,因为它在私有范围内。 我们只能访问那些在返回对象中声明的函数, 因为它是公共函数的表示


0

揭示模块的基本概念是,您有一个对象,它封装其数据和行为:

var Module = (function(){
    var privateStuff = {};
    var publicStuff = {};

    return publicStuff;
})();

然而,在使用这种模式时,有一些最佳实践应该采用。以下是一个模块(“Modulus”),它具有一些属性,仅供演示目的,并采用了其中一些实践:

function AbstractSomeClass(id) {
    this.id = id;
    return this;
}

var Modulus = (new (function SomeClass() {
    var thus = this;

    function NameClass(name){
        this.value = thus.name || name;
    }

    AbstractSomeClass.call(this, 998);

    this.name = 'Touring';
    this.name = ( new NameClass('Hofstadter') ).value;

    return {
        id: this.id,
        name: this.name
    };
})());

注意使用 (new (function SomeClass(){ ... })()); 语法。像这样使用 new 允许你在闭包内部使用 this 关键字。如果你需要从另一个类继承属性 (AbstractSomeClass.call(this, 998);),这非常方便 -- 然而,你仍然需要 公开 你想要公开的属性,例如:

return {
    id: this.id,
    name: this.name
};

还要注意我们将this分配给thus——这使我们能够在具有自己的this作用域的子类中使用父级thisthis.value = thus.name || name;
再次强调,这些只是建议的惯例和最佳实践。

0

对于模块外部的代码来说,这并没有什么区别。在那篇文章中的所有3种情况下,方法的调用方式都是相同的。但是模块本身的结构在内部是不同的。

Crockford的模块模式和他们所谓的“揭示模块模式”在结构上基本上是相同的。唯一的区别是它们首先将方法分配给一个本地变量,以便更易读。但实际上并没有什么特别之处,你在链接中就有一些例子。


使用揭示模块模式返回的对象在覆盖方面与 Douglas Crockford 的模块模式行为不同(请参见 Addy Osmani 在 http://addyosmani.com/resources/essentialjsdesignpatterns/book/#revealingmodulepatternjavascript 上的警告)。 - I-Lin Kuo

0

您的链接已损坏。 - klewis
感谢@klewis指出这一点。我已经更新了链接https://tarunnagpal78.medium.com/revealing-module-design-pattern-in-javascript-3b0db0bcd121 - Tarun Nagpal

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接