JavaScript利用闭包实现模块化


Posted in Javascript onJanuary 13, 2017

利用闭包的强大威力,但从表面上看,它们似乎与回调无关。下面一起来研究其中最强大的一个:模块。

function foo() {
 var something = "cool";
 var another = [1, 2, 3];
 function doSomething() {
 console.log( something );
 }
 function doAnother() {
 console.log( another.join( " ! " ) );
 }
}

正如在这段代码中所看到的,这里并没有明显的闭包,只有两个私有数据变量something和another,以及doSomething() 和doAnother() 两个内部函数,它们的词法作用域(而这就是闭包)也就是foo() 的内部作用域。

接下来考虑以下代码:

function CoolModule() {
 var something = "cool";
 var another = [1, 2, 3];
 function doSomething() {
 alert( something );
 }
 function doAnother() {
 alert( another.join( " ! " ) );
 }
 return {
 doSomething: doSomething,
 doAnother: doAnother
 };
}
var foo = CoolModule();
foo.doSomething(); // cool
foo.doAnother(); // 1 ! 2 ! 3

这个模式在JavaScript 中被称为模块。最常见的实现模块模式的方法通常被称为模块暴露,这里展示的是其变体。我们仔细研究一下这些代码。

首先,CoolModule() 只是一个函数,必须要通过调用它来创建一个模块实例。如果不执行外部函数,内部作用域和闭包都无法被创建。其次,CoolModule() 返回一个用对象字面量语法{ key: value, ... } 来表示的对象。这个返回的对象中含有对内部函数而不是内部数据变量的引用。我们保持内部数据变量是隐藏且私有的状态。可以将这个对象类型的返回值看作本质上是模块的公共API。这个对象类型的返回值最终被赋值给外部的变量foo,然后就可以通过它来访问API 中的属性方法,比如foo.doSomething()。

从模块中返回一个实际的对象并不是必须的,也可以直接返回一个内部函数。jQuery 就是一个很好的例子。jQuery 和$ 标识符就是jQuery 模块的公共API,但它们本身都是函数(由于函数也是对象,它们本身也可以拥有属性)。

doSomething() 和doAnother() 函数具有涵盖模块实例内部作用域的闭包( 通过调用CoolModule() 实现)。当通过返回一个含有属性引用的对象的方式来将函数传递到词法作用域外部时,我们已经创造了可以观察和实践闭包的条件。如果要更简单的描述,模块模式需要具备两个必要条件。

1. 必须有外部的封闭函数,该函数必须至少被调用一次(每次调用都会创建一个新的模块实例)。

2. 封闭函数必须返回至少一个内部函数,这样内部函数才能在私有作用域中形成闭包,并且可以访问或者修改私有的状态。

一个具有函数属性的对象本身并不是真正的模块。从方便观察的角度看,一个从函数调用所返回的,只有数据属性而没有闭包函数的对象并不是真正的模块。上一个示例代码中有一个叫作CoolModule() 的独立的模块创建器,可以被调用任意多次,每次调用都会创建一个新的模块实例。当只需要一个实例时,可以对这个模式进行简单的改进来实现单例模式:

var foo = (function CoolModule() {
 var something = "cool";
 var another = [1, 2, 3];
 function doSomething() {
 alert( something );
 }
 function doAnother() {
 alert( another.join( " ! " ) );
 }
 return {
 doSomething: doSomething,
 doAnother: doAnother
 };
})();
foo.doSomething(); // cool
foo.doAnother(); // 1 ! 2 ! 3

立即调用这个函数并将返回值直接赋值给单例的模块实例标识符foo。

模块也是普通的函数,因此可以接受参数:

function CoolModule(id) {
 function identify() {
 console.log( id );
 }
 return {
 identify: identify
 };
}
var foo1 = CoolModule( "foo 1" );
var foo2 = CoolModule( "foo 2" );
foo1.identify(); // "foo 1"
foo2.identify(); // "foo 2"

模块模式另一个简单但强大的变化用法是,命名将要作为公共API 返回的对象:

var foo = (function CoolModule(id) {
function change() {
 // 修改公共API
 publicAPI.identify = identify2;
}
function identify1() {
 alert( id );
}
function identify2() {
 alert( id.toUpperCase() );
}
var publicAPI = {
 change: change,
 identify: identify1
};
return publicAPI;
})( "foo module" );
foo.identify(); // foo module
foo.change();
foo.identify(); // FOO MODULE

通过在模块实例的内部保留对公共API 对象的内部引用,可以从内部对模块实例进行修改,包括添加或删除方法和属性,以及修改它们的值。

以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,同时也希望多多支持三水点靠木!

Javascript 相关文章推荐
javascript 短路法代码精简
Aug 20 Javascript
写给想学习Javascript的朋友一点学习经验小结
Nov 23 Javascript
AJAX跨域请求json数据的实现方法
Nov 11 Javascript
jqGrid随窗口大小变化自适应大小的示例代码
Dec 28 Javascript
每天一篇javascript学习小结(Function对象)
Nov 16 Javascript
老生常谈原生JS执行环境与作用域
Nov 22 Javascript
JS回调函数简单用法示例
Feb 09 Javascript
详解Vue使用命令行搭建单页面应用
May 24 Javascript
JS 验证密码 不能为空,必须含有数字、字母、特殊字符,长度在8-12位
Jun 21 Javascript
vue-cli项目中使用公用的提示弹层tips或加载loading组件实例详解
May 28 Javascript
JS实现获取数组中最大值或最小值功能示例
Mar 02 Javascript
vue实现验证用户名是否可用
Jan 20 Vue.js
Vue.js基础知识小结
Jan 13 #Javascript
canvas实现流星雨的背景效果
Jan 13 #Javascript
JavaScript正则表达式替换字符串中图片地址(img src)的方法
Jan 13 #Javascript
原生js实现手风琴功能(支持横纵向调用)
Jan 13 #Javascript
JavaScript使用简单正则表达式的数据验证功能示例
Jan 13 #Javascript
bootstrap网格系统使用方法解析
Jan 13 #Javascript
js 判断数据类型的几种方法
Jan 13 #Javascript
You might like
落伍首发 php+mysql 采用ajax技术的 省 市 地 3级联动无刷新菜单 源码
2006/12/16 PHP
php数组中删除元素的实现代码
2012/06/22 PHP
php生成短网址示例
2014/05/05 PHP
JavaScript 小型打飞机游戏实现原理说明
2010/10/28 Javascript
js改变img标签的src属性在IE下没反应的解决方法
2013/07/23 Javascript
限制textbox或textarea输入字符长度的JS代码
2013/10/16 Javascript
jQuery抛物线运动实现方法(附完整demo源码下载)
2016/01/08 Javascript
javascript高级选择器querySelector和querySelectorAll全面解析
2016/04/07 Javascript
Vue.js实战之通过监听滚动事件实现动态锚点
2017/04/04 Javascript
Angular 2父子组件数据传递之局部变量获取子组件其他成员
2017/07/04 Javascript
vue综合组件间的通信详解
2017/11/06 Javascript
在JS循环中使用async/await的方法
2018/10/12 Javascript
前端Vue项目详解--初始化及导航栏
2019/06/24 Javascript
layui实现tab的添加拒绝重复的方法
2019/09/04 Javascript
JS通过识别id、value值对checkbox设置选中状态
2020/02/19 Javascript
VUE-ElementUI 自定义Loading图操作
2020/11/11 Javascript
[04:22]DSPL第二期精彩集锦:残血反杀!
2014/12/10 DOTA
[41:05]Serenity vs Pain 2018国际邀请赛小组赛BO2 第二场 8.19
2018/08/21 DOTA
Python基于回溯法子集树模板解决选排问题示例
2017/09/07 Python
linecache模块加载和缓存文件内容详解
2018/01/11 Python
python 3.3 下载固定链接文件并保存的方法
2018/12/18 Python
python交互模式下输入换行/输入多行命令的方法
2019/07/02 Python
pytorch 在网络中添加可训练参数,修改预训练权重文件的方法
2019/08/17 Python
Python使用matplotlib绘制圆形代码实例
2020/05/27 Python
python解压zip包中文乱码解决方法
2020/11/27 Python
深入理解HTML的FormData对象
2016/05/17 HTML / CSS
俄罗斯儿童和青少年服装、鞋子及配件的在线商店:Orby
2020/02/20 全球购物
Envie de Fraise意大利:法国网上推出的孕妇装品牌
2020/10/18 全球购物
《一个小村庄的故事》教学反思
2014/04/13 职场文书
在校实习生求职信
2014/06/18 职场文书
经济贸易系求职信
2014/08/04 职场文书
2015年学校教科室工作总结
2015/07/20 职场文书
2015年教务处干事工作总结
2015/07/22 职场文书
SpringBoot整合JWT的入门指南
2021/06/29 Java/Android
Win11 Build 25179预览版发布(附更新内容+ISO官方镜像下载)
2022/08/14 数码科技
Win11 Beta 22621.601 和 22622.601今日发布 KB5017384修复内容汇总
2022/09/23 数码科技