65.9K
CodeProject 正在变化。 阅读更多。
Home

JavaScript 模块模式

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.35/5 (21投票s)

2011年8月29日

CPOL

6分钟阅读

viewsIcon

153561

downloadIcon

662

JavaScript 模块模式有助于以面向对象的方式编写代码,并具有很高的网页性能。

引言

在本文中,我将讨论 JavaScript 中一种最佳编码实践,即模块模式。

模块模式在 JavaScript 世界中非常普遍,具有重要意义。它帮助我们编写简洁的面向对象的 JavaScript。模块模式限制了开发者创建多个全局变量。为了获得良好的 Web 应用程序性能,拥有较少的全局变量始终是一种良好的实践。因此,该模式提高了您网站的性能,同时提供了易于维护的面向对象的编程实践。

什么是模块模式

在 JavaScript 编程语言中,函数可以用作模块。有时,需要创建单例对象而不是类实例。在模块内部,内部函数与其他已定义变量和函数具有静态作用域。

用 JavaScript 语言编写的大多数应用程序都是单例。因此,它们都以模块模式编写。

模块可以被视为 C# 中的单例类。在模块中,定义的所有变量仅在模块中可见。模块中的方法具有作用域,可以访问共享的私有数据和私有方法。因此,这些方法也称为特权方法

因此,通过创建一个匿名函数,以简单的对象字面量格式返回一组特权方法。由于闭包原理,这些特权方法可以访问匿名函数的内部方法和变量。我们称之为模块模式

特权方法

在模块模式中,您可以编写您不想暴露给外部世界的私有方法和私有变量,同时您还可以编写公共和特权方法。此模式的巨大用处在于,您可以从称为特权方法的方法中很好地访问您的私有方法和变量。

闭包

要学习模块模式,必须了解闭包原理。通过使用闭包,模块化模式变得非常有用的和强大的。闭包原理指出,父函数内的任何内部函数在父函数返回后仍然可以访问其他内部函数和变量。在模块模式中,特权方法可以访问模块的其他私有变量和方法。

自执行函数

在模块模式中,我们通过编写自执行方法来创建一个全局单例对象。自执行方法是一个仅由自身调用的匿名方法。请看下面的代码,它解释了如何编写自执行方法。

自执行函数示例

(function ( ) { 
    //create variable
    //var name = "rupesh";
    //create method
    //var sayName = function ( ) {
    //    alert(name);
    //};
})( );//calling this method.  

在上面的代码中,我有一个匿名方法,它已被声明,并在声明后立即通过writing();调用。执行此 JavaScript 代码的瞬间,该方法就会自行执行。因此,它被称为自执行方法。

模块模式示例

在理解了模块模式、自执行函数和闭包的概念后,现在我来定义模块模式的风格。请看下面的代码

//Single Global Variable "Module"
var Module = ( function ( ) {
    var privateVariable = "some value",
        privateMethod = function ( ) {
			//do something.
	};
    //returning one anonymous object literal that would expose privileged methods.
    return {
         //the method inside the return object are 
         //called as privileged method because it has access 
         //to the private methods and variables of the module.

         privilegedMethod : function ( ) {
            //this method can access its private variable and method 
            //by using the principle of closure. 
            alert(privateVariable); //accessing private variable.
            privateMethod( ); //calling private method
        }
    };
})( ); 

在这里,Module 是暴露给文档的单个全局变量。我们正在声明、调用一个匿名方法,并将其分配给 Module 变量。现在我们可以通过编写 Module.privilegedMethod(); 来调用 privilegedMethod 。在模块内部,特权方法可以访问其私有变量和私有方法。因为它们属于其静态作用域。如果我们有任何不想暴露的数据或方法,可以将其放入私有方法中。

我们也可以用不同的方式编写上面的代码,请看下面的方法

//Single Global Variable "Module"
var Module = ( function ( ) {
    var 
     privateVariable = "some value"
    ,privateMethod = function ( ) {
        //do something.
    }
    //object literal that would have privileged methods.
    ,retObject = {
         //the method inside the return object are 
         //called as privileged method because it has access 
         //to the private methods and variables of the module.
         privilegedMethod : function ( ) {
            //this method can access its private variable and method 
            //by using the principle of closure. 
            alert(privateVariable); //accessing private variable.
            privateMethod( ); //calling private method
        }
    };
    //returning the object.
    return retObject;
})( ); 

公共方法无法访问私有方法和变量,只有模块内的特权方法由于闭包才能访问。我们来看看。

我们可以通过以下方法在模块中创建公共方法

//augmenting public method in the module
Module.publicMethod1 = function () {
    //do something...
    //However here we will not have access to the private methods of the Module.
    //We can call the privileged method of the module object from here.
    //And by the privileged method we can call or access the inner private
    // methods and variables correspondingly 
    Module.privateMethod ( ); // this is not possible.
    //calling a privileged method of module 
    Module.privilegedMethod ( ); // this can be possible.   
}

在上面的示例中,我们添加到模块的公共方法 publicMethod1 无法访问 Module 对象的 privateMethod 。为了访问私有方法或变量,我们可以调用模块的特权方法(在公共方法内部)。这是此模式的好处。在上面的示例中,我们可以调用 Module.privilegedMethod()

模块模式示例

我创建了一个示例 .NET 应用程序,该应用程序已附加到本文中。在该 .NET 应用程序中,我创建了一个模块模式 JavaScript 函数。让我们来讨论一下这个函数。请看下面的代码片段

//One Global object exposed. 
var SearchEngine = (function ( ) { 
    //Private Method.
    var luckyAlgo = function ( ){
        //create one random number.
        return Math.floor(Math.random()*11);
    }
    //Returning the object
    return {
        //privileged method.
        getYourLuckyNumber : function ( ){
            //Has access to its private method because of closure.
            return luckyAlgo();        
        }
    } 
} ) ( );//Self executing method.

在上面的代码片段中,我有一个名为 SearchEngine 的全局变量。自执行的匿名方法被赋值给 SearchEngine 变量。

SearchEngine 有一个私有方法 luckyAlgo 和一个特权方法 getYourLuckyNumber 。特权方法通过封装在匿名对象中返回。SearchEngine 可以全局访问,它将只有一个特权方法。特权方法可以调用其本地方法 luckyAlgo ,这是因为闭包原理。在上面的示例中,如果您调用 getYourLuckyNumber 方法,它将返回一个随机数。

创建子模块

我们也可以通过将子模块添加到我们的模块对象来创建子模块。它将以相同的模块模式形式。请看下面的示例,我通过遵循相同的模块模式在 SearchEngine 对象中创建了一个名为 subSearch 的对象。

//Augmenting the module with submodule
SearchEngine.subSearch = (function ( ) {
   //Private variable.
   var defaultColor = "Orange";
    //private method.
    var myColorAlgo = function (num) {
        switch(num){
            case 1:
            defaultColor ="Green";break;
            case 2:
            defaultColor ="Black";break;
            case 3:
            defaultColor ="Yellow";break;
            case 4:
            defaultColor ="White";break;
            case 9:
            defaultColor ="Red";break;
        }
    };
    return {
    //privileged method
        getYourLuckyColor : function(){
            //access to private variable because of closure.
            myColorAlgo(SearchEngine.getYourLuckyNumber());
            return defaultColor;
        }
    };
})();

在上面的示例中,如果我们调用 getYourLuckyColor 方法,它将根据随机数返回一个颜色名称。我知道这些方法不是很有用,但我只能想到它们是为了解释概念。

扩展模块

我们可以通过将现有模块包装在一个新模块中来扩展现有模块。通过这样做,我们可以很好地访问/覆盖旧模块的现有方法。请看下面的代码

var Module1 = ( function (oldModule) {
    var 
	//assigning oldmodule in to a local variable.
	parent = oldModule;
	
    //overriding the existing privileged method.
    parent.privilegedMethod = function ( ){
         //do something different by overriding the old method.
    };
	
    //private method accessing privileged method of parent module.
    var privateMethod2 = function ( ) {
        parent.privilegedMethod();//can access privileged method of Module
        parent.publicMethod1(); //can access public method of Module
    }
    return {
        newMethod : function ( ) {
          ///do something for this brand new module.
          ///use some functionality of parent module.
          /// parent.privilegedMethod( );
        }
    };
} )(Module);//Module object is the existing module that I want to extend. 

在上面的示例中,我描述了模块模式的高级功能,即可以扩展、增强和覆盖现有模块来重用它。

我将旧的现有 Module 对象(请参阅上面模块模式部分,我们创建了一个 Module 对象)传递给我们的匿名方法,然后将其分配给一个名为 parent 的局部变量。现在,我可以在新 Module1 对象的私有、特权和公共方法中访问父 Module 公共/特权方法。

此模式的精妙之处在于,我还可以覆盖旧 Module 的现有方法。您可以看到,我已经覆盖了 Module 对象的 privilegedMethod。同样,我也可以覆盖 Module 对象的 public 方法。

结论

我给出了非常简单的例子。但是,一个人可以学习这个模式并创建非常复杂的代码。您必须了解 YUI,这是一个很棒的 JavaScript 库,它遵循模块模式的原理。根据我的个人经验,我过去经常使用模块模式编写 JavaScript 代码,而且我非常喜欢它。现在这个模式是我最喜欢的,我也从中获得了很多乐趣。希望这篇文章能帮助您。请留下您的评论和建议。

© . All rights reserved.