为什么是模块模式

why module pattern?

本文关键字:模式 模块 为什么      更新时间:2023-09-26

我读过很多关于模块模式的东西。好的,它带来了结构,私有方法等。但是使用下面的代码,我可以在不使用它的情况下获得相同的行为。

function Human()
{
  // private properties
  var _name='';
  var _age=0;

  // private methods
  function created()
  {
    console.log("Human "+_name+" called");
  };
  // public
  this.setName = function(name){
    _name=name;
    created(); 
  };
}

var h1 = new Human();
h1.setName("John");

那么,模块模式的真正优势是什么呢?

我认为这个例子可以帮助您澄清模块模式的有用性。

模块模式
模块模式被广泛使用,因为它提供了结构和帮助组织随着代码的增长。与其他语言不同,JavaScript没有特殊的语法对于包,但是模块模式提供了创建自包含解耦的工具代码片段可以被视为功能的黑盒,根据软件的(不断变化的)需求替换或删除你写作。

模块模式是几个模式的组合,即:

    名称空间
  • <
  • 直接功能/gh>
  • 私人和特权成员
  • <
  • 声明依赖/gh>

第一步是设置名称空间。让我们使用前面提到的命名空间()函数在本章中,我们开始一个示例实用模块,它提供了有用的数组方法:

MYAPP.namespace('MYAPP.utilities.array');

下一步是定义模块。该模式使用一个即时函数,它将如果需要隐私,则提供私有范围。immediate函数返回一个对象——具有公共接口的实际模块,它将对消费者可用模块:

 MYAPP.utilities.array = (function () {
    return {
    // todo...
    };
 }());

接下来,让我们向公共接口添加一些方法:

MYAPP.utilities.array = (function () {
   return {
      inArray: function (needle, haystack) {
         // ...
      },
      isArray: function (a) {
         // ...
      }
   };
}());

使用由immediate函数提供的私有作用域,您可以声明一些根据需要使用私有属性和方法。就在直接函数的最上面也将是声明模块可能具有的任何依赖项的地方。后在变量声明中,可以选择性地放置任何一次性初始化代码帮助设置模块。最终结果是直接函数返回的对象包含你的模块的公共API:

MYAPP.namespace('MYAPP.utilities.array');
MYAPP.utilities.array = (function () {
   // dependencies
   var uobj = MYAPP.utilities.object,
       ulang = MYAPP.utilities.lang,
       // private properties
       array_string = "[object Array]",
       ops = Object.prototype.toString;
       // private methods
       // ...
       // end var
   // optionally one-time init procedures
   // ...
   // public API
   return {
      inArray: function (needle, haystack) {
         for (var i = 0, max = haystack.length; i < max; i += 1) {
            if (haystack[i] === needle) {
               return true;
            }
         }
      },
      isArray: function (a) {
         return ops.call(a) === array_string;
      }
      // ... more methods and properties
   };
}());
模块模式是一种广泛使用且强烈推荐的组织的方法代码,特别是随着代码的增长。

" JavaScript Patterns,作者:Stoyan Stefanov(O ' reilly)。雅虎版权所有

不知道为什么没有人正确回答这个问题。我可以看到在某种模式中使用自动调用函数以使私有变量可继承的可能性,但是您是完全正确的。

用模块模式代替核心语言的函数构造函数没有任何好处。这是完全相同的语言机制(闭包),它允许持久的内部变量作为不可访问的实体存在,只是需要更多的代码。

在JS中,函数构造函数遵循与触发函数相同的作用域规则。范围和闭包是在定义时设置的。函数构造函数中的内部var存在的原因是,在同一个构造函数中定义了方法的实例引用了该var存在的实例。

唯一改变的是你已经在构造函数中消除了原型方法的使用,并且必须为继承的方法自行修改继承机制。

庞大的JavaScript项目正在使用模块模式构建,也许你有自己的想法,并希望确保你的代码易于阅读和结构良好。通过学习一些模式,你可以使你的应用程序更容易构建和维护。这些模式被大多数JavaScript天才开发人员所使用,例如构建node js、JQuery、bootstrap、angular等。简单的例子:

  function foo (){
    console.log("foobar");
};
foo();

当我们调用这个简单的方法时,我们会注意到它可以在全局命名空间中访问,当我们谈论大型应用程序时,这是一个巨大的问题。

有时我们将其称为混乱的全局名称空间。这是一个问题,因为浏览器中发生了很多事情,你可能会在浏览器中加载angular, bootstrap和Jquery,你甚至可能有一些其他的javascript库。现在,如果您必须导航一堆使全局名称空间混乱的变量,那么它可能就没有意义了。事实上,如果您创建的变量与其他库中的变量共享相同的名称,甚至可能是灾难性的。最基本的模块模式将解决这个问题。为了做到这一点,我在一个自我执行的匿名函数中描述了我的代码,现在看起来像这样:From this:

function foo (){
    console.log("foobar");
};
foo();

To this:

(function(){function foo (){
    console.log("foobar");
};
foo();
}());

我们包装一个外壳()函数然后我可以称呼它,原因需要包装外壳是因为函数本身没有名字将返回未定义,然而当我们包装函数括号本质上是说把整件事当做一个表达式是立即评估,并不一定需要一个命名或任务,当我们运行代码并检查浏览器现在我们会发现变量全局命名空间并不杂乱。我们可以看到foo是undefined

我可以想到5点使用你的类(喜欢)的例子和标准的模块化模式。

1。多个实例:使用类可以创建多个实例。

    function Human() {
        // private properties
        var _name = 'Bob'; // Each instance will gets a new private member copy
        // public for the created instance
        this.setName = function (name) { 
            _name = name;
        };
        this.getName = function () {
            return _name;
        };
        // setName() & getName() - can be moved to prototype for saving memory
    }
    // Instance - 1
    var h1 = new Human();
    h1.setName("John");
    console.log(h1.getName()); // John
   // Instance - 2
    var h2 = new Human();
    console.log(h2.getName()); // Bob  (NOTE: not John)

2。在上面的例子中,Human()类中的所有成员将为每个新创建的实例获得一个新的副本(不共享)。

让我们将上面的例子转换成模块模式:

    var Human = (function() {
        // private properties
        var _name = 'Bob';
        // private
        setName = function (name) {
            _name = name;
        };
        // private
        getName = function () {
            return _name;
        };
        return { 
            setName: setName,
            getName: getName 
        };
    })();

    Human.setName('John');
    console.log(Human.getName()); // John
    Human.setName('Bob');
    console.log(Human.getName()); // Bob
  • 在上面的模块化模式示例中,我们不能像上面的类示例那样创建任何实例。这里的模式允许您以一种有组织的方式(不污染全局作用域)通过'Human'设置/获取值。

  • 在模块化模式中不能创建多个实例吗?可以,可以通过构造函数和原型实现。

  • 让我们向模块化模式中添加构造函数和原型:

            var Human = (function () {
            // private properties
            var _name = 'Bob'; // Shared across all the instance
            // private
            setName = function (name) {
                _name = name;
            };
            // private
            getName = function () {
                return _name;
            };
            // constructor
            var Human = function () { };
            // prototype
            Human.prototype = {
                constructor: Human,
                setName: setName,
                getName: getName
            };
            return Human;
        })();
        // Instance - 1
        var h1 = new Human();
        h1.setName("John");
        console.log(h1.getName()); // John
        // Instance - 2
        var h2 = new Human();
        console.log(h2.getName()); // John (NOTE: Not Bob bcos it uses the shared copy)
    

    5。从上面的例子中你可以看到,与普通类不同,上面的模块化模式为所有实例使用了共享的私有成员副本(在示例点1中:每个实例都有它自己的私有成员副本)。