JavaScript设计模式(2)——Module(模块)模式

JavaScript中的模块实现方法有:

  • 对象字面量(本篇介绍)
  • Module模式(下篇介绍)
  • AMD模块(无)
  • CommonJS模块(无)
  • ECMAScript Harmony模块(无)

模块通常用来分离和组织项目中的代码单元。

1. 对象字面量

对象字面量表示法中,一个对象被描述为一组包含在大括号({})中,以逗号分割的name/value对。 对象内的名称可以是字符串或者标识符,用冒号结尾。 最后一个name/value对后面不能加逗号。

var myObjectLiteral = {
    variableKey:variableValue,
    functionKey:function(){
        //...
    }
};

对象字面量不需要使用new关键字来进行实例化。 使用对象字面量表示的模块示例:

var myModule = {
    // 对象字面量可以拥有属性
    myProperty : "someValue",

    // 对象字面量可以包含对象
    myConfig : {
        useCaching: true,
        language: "en"
    }

    // 可以拥有方法
    myMethod : function(){
        console.log("this is a message from a method in mosule");
    }

    // 根据当前配置输出信息
    myMethod2 : function(){
        console.log("Caching is: " + this.myConfig.useCaching ? "enabled" : "disabled");
    }

    // 重写当前配置
    myMethod3 : function(newConfig){
        if(typeof newConfig === "object"){
            this.myConfig = newConfig;
            console.log(this.myConfig.language);
        }
    }
};

// 输出this is a message from a method in mosule
myModule.myMethod();

// 输出enable
myModule.myMethod2();

// 输出cn
myModule.myMethod3({
    language: "cn",
    useCaching : false
});

2. Module(模块)模式

JavaScript中,Mosule模式用于模拟类的概念,这种方式能够使一个单独的对象拥有公有/私有方法和变量,从而屏蔽来自全局作用域的特殊部分。 可以使得函数名于页面其他脚本定义的函数冲突的可能性降低。

2.1 私有

JavaScript中没有真正意义上的私有,因为它没有访问修饰符。因此需要使用函数作用域来模拟这一概念。 Module模式使用闭包封装“私有”状态和组织,它提供了一种包装混合私有/公有的方法和变量的表达式,防止其泄露到全局作用域。 通过闭包,暴露一个公有API,其他的部分维持在私有闭包中。

2.2 示例

  1. 自包含的模块实现Module模式
var testModule = (function(){
    var counter = 0;
    return {
        incrementCounter: function(){
            return ++counter;
        },

        resetCounter: function(){
            console.log("counter value prior to reset: "+ counter);
            counter = 0;
        }
    };
})();

// 用法:

//增加计数器
testModule.incrementCounter();

// 检查计数器并重置
// 输出:counter value prior to reset: 1
testModule.resetCounter();

// 输出:undefined
console.log(testModule.counter);

在这里,代码外部无法直接读取incrementCounter()或者resetCounter()。也无法访问其中的counter变量,这样它表现的就像一个私有变量。

  1. 一个包含命名空间,公有和私有变量的Module模式
var myNameSpace = (function(){
    // 私有计数器变量
    var myPrivateVar = 0;

    // 记录所有参数的私有函数
    var myPrivateMethod = function(foo){
        console.log(foo);
    };

    return {
        //公有变量
        myPublicVar : "foo",
        myPublicFunction : function(bar){
            // 私有变量自增
            myPrivateVar++;

            // 传入bar调用私有方法
            myPrivateMethod(bar);
        }
    };
})();

在这些例子中,模块中的作用域函数包裹在所有的函数周围,然后调用并立即存储返回值,这样有很多优点,包括:

  • 只有该模块才有使用私有函数的自由,因为这些函数不会暴露在页面的其他部分,暴露出来只有输出的API
  • 鉴于函数往往函数已经声明并命名,在试图找到函数抛出的异常时,这将使调制器中显示调用堆栈显得容易
  • 根据环境,可以返回不同的函数

3. 模式变化

3.1 引入混入

下面的例子演示了全局变量(jQuery,Underscore)如何作为参数传递给模块的匿名函数。我们引入它们,并给它们取一个本地别名。

// 全局模块
var myModule = (function(jQ, _){
    function privateMethod1(){
        jQ(".container").html("test");
    }

    function provateMethod2(){
        console.log(_.min([10, 5, 100, 2, 1000]}));
    }

    return {
        publicMethod: function(){
            privateMethod1();
        }
    };
    // 引入jQuery和Uderscore
})(jQuery, _);

myModule.publicMethod();

3.2 引出

下面的例子声明了一个全局变量,而不需要实现它们,并可以同样地作为全局变量引入到模块中。

// 全局模块
var myModule = (function(){
    // 模块对象
    var module = {},

    // 私有属性
    var privateVariable = "asd";

    //私有方法
    function provateMethod(){
        // ...
    }

    module.publicProperty = "FooBar";
    module.publicMethod = function(){
        console.log(privateVariable);
    }
    return module;
})();

3.3 特定框架下的Module模式实现

这里仅介绍jQuery下Module模式的实现,示例中定义了library函数,声明一个新库,并在创建新库时将init函数自动绑定到document.ready。

function library(module){
    $(function(){
        if(module.init()){
            module.init();
        }
    });
    return module;
}

var myLibrary = library(function(){
    return {
        init:function(){
            // module implementation
            // 模块实现
        }
    };
})();

3.4 优点

首先,对于面向对象来说,比起真正的封装思想,从JavaScript的角度而言是很简洁的。 其次,它支持了私有数据

3.5 缺点

由于我们访问私有和公有成员的方法不同,当我们想改变可见性时,实际上我们要修改每一个使用过成员变量的地方。 此外,我们也无法访问那些在方法里添加的私有成员。 其他缺点包括:

  • 无法为私有成员创建自动化单元测试,bug需要增加补丁时会增加额外的复杂性。

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏mwangblog

Dijkstra算法

Dijkstra算法使用了广度优先搜索解决赋权有向图(或无向图)的单源最短路径问题。

31430
来自专栏前端达人

【ES6基础】Symbol介绍:独一无二的值

ES6之前我们都清楚JS有六种数据类型:Undefined、Null、布尔值(Boolean)、字符串(String)、数值(Number)、对象(Object...

15270
来自专栏大数据文摘

“我比Goodfellow提前三年想到了GAN”

2014年的一晚,Ian Goodfellow和一个刚刚毕业的博士生一起喝酒庆祝。在蒙特利尔一个酒吧,一些朋友希望他能帮忙看看手头上一个棘手的项目:计算机如何自...

12830
来自专栏京程一灯

Chrome 74 带来的新功能[每日前端夜话0x66]

Chrome 74 已经发布了,虽然从用户的角度来看并没有什么令人兴奋的东西,但是对开发人员来说带来了一些好处。新版本附带了新的 Javascript 私有类字...

13320
来自专栏银河系资讯

Python列表推导(list comprehension)VS 生成器表达式(generator expression

列表是一种可以表示为元素集合的数据。一个简单的列表如下所示:[0, 1, 2, 3, 4, 5] 列表将所有可能类型的数据和数据组合作为其元素:

13210
来自专栏CWIKIUS

Angular 文档中链接的修改路径

左侧链接的修改路径在 angular-cn\aio\content\navigation.json 这个文件中。

10940
来自专栏京程一灯

13个帮你提高开发效率的现代CSS框架[每日前端夜话0x67]

本文将向你介绍一系列顶级CSS框架。有些人可能听说过,也可能对有些人是全新的。但它们都提供了各种有用的先进功能,可以改善你的工作流程。开始吧!

13240
来自专栏量子位

你比个手势,AI就识别出Emoji,浏览器上跑:已开源,推特2.8万赞

AI的爸爸,名字叫Nick Bourdakos (简称“尼克”) ,是来自IBM的程序猿。

27420
来自专栏腾讯NEXT学位

干货 | 小程序多页面接口数据缓存

小程序里面多个页面,有时候会需要用到同一个接口的数据。而这些数据全局来说只需要拉取一遍,如果要存到缓存,要怎么保证其他页面取缓存的时候,数据已经拉取回来了呢?

22240
来自专栏腾讯开源的专栏

【开源公告】微信智聆口语评测小程序开源

由微信智聆语音团队研发的智聆口语评测小程序插件,能够对学习者的发音进行自动评测打分,检测发音中存在的错误。评测人群支持从儿童到成人年龄全覆盖;评测方式涵盖单词、...

46730

扫码关注云+社区

领取腾讯云代金券

年度创作总结 领取年终奖励