说三道四技术文摘-感悟人生的经典句子
说三道四 > 文档快照

全面解析ECMAScript 6模块系统

HTML文档下载 WORD文档下载 PDF文档下载
模块化对任何大型应用来说十分必要,ES6弥补了JavaScript缺少对模块支持的遗憾。浏览器虽对ES6模块化支持还不够,但开发者可以借助编译器将ES6模块转换成ES5。本文将系统讲解ES6模块系统所提供的各种编码方法。

【编者按】在任何一个大型应用中模块化是很常见的。ES6的模块为JavaScript提供了这个特性,并且为这些模块提供了许多选择方法来导出和引入对象。Ravi Kiran 在《Modules in ECMAScript 6 (ES6)》一文中主要讨论了ES6模块系统。以下为该文章的简译内容:

无论使用何种编程语言开发大型应用,最关键的特性就是代码模块化。这个概念在不同的编程语言里有着不同的命名,在C里为头部文件,C++和C#里为命名空间,Java中为包,名称不一样但解决的是同一问题。正如《 ECMAScript 6 – New language improvements in JavaScript》系列文章中第一篇所提到的那样,最初JavaScript并不是用来编写大量代码的,比如创建大型框架、App应用等。就在我们因为JavaScript缺少对模块的支持而编写大量代码时,开源开发者提出了一些标准,如CommoneJs模块模型、异步模块定义(AMD)以及一些库,来实现模块化。在过去几年里,这些库获得了广泛关注,并成功应用到多个企业规模级的应用程序中。

ES6为JavaScript带来了模块特性。浏览器实现这一特性还需要一段时间,因为它们必须定义一个方法来动态下载文件。在浏览器支持该特性以前,我们可以使用编译器,如 Traceur、6to5、ES6 Module Loader以及其它可以让ES6模块转换成ES5的转码器。

JavaScript模块系统的现状

CommonJS模块系统

CommonJs是一个由开源开发者组成的团队,主要围绕JavaScript实现一些API及开展研发实践。该团队提出了一个JavaScript模块规范。每个文件都可当作一个模块,并且每个文件可以访问两个对象:require和export。require用来接收字符串(模块名),并返回该模块输出的对象。export对象用来导出该模块的方法和变量。require方法返回的就是export对象。模块同步加载。服务器端JavaScript引擎Node.js就是用的这个模块系统。

异步模块定义(AMD)

AMD是一个采用异步方式加载依赖模块的模块系统。如果模块在不同文件中,它们将采用XHR进行加载。某一模块将等其所依赖的模块一一加载后才会被执行。AMD模块必须是一个函数,并作为参数传入define函数中。函数的返回值将传输给所有依赖的模块,所获得返回值又将作为参数传给模块方法。Require.js库中实现了AMD。

TypeScript模块

TypeScript,作为JavaScript的超集,也提供了一个模块系统。当它被编译时,便开始使用JavaScript模块模式。TypeScript模块使用module关键字定义,任何被输出的对象必须使用export关键字定义。import关键字用来将其它模块加载入模块中,并捕捉该模块导出的对象。TypeScript模块是同步加载的。

ES6模块系统

ES6模块系统启发于上述现有模块系统,它具有以下特性:

  1. 使用export关键词导出对象。这个关键字可以无限次使用;
  2. 使用import关键字将其它模块导入某一模块中。它可用来导入任意数量的模块;
  3. 支持模块的异步加载;
  4. 为加载模块提供编程支持。

接下来让我们通过具体编程方法看看每一个特性。

导出对象

在现有的模块系统中,每个JavaScript代码文件在ES6中都是一个模块。只有模块中的对象需要被外部调用时,模块才会输出对象,其余则都是模块的私有对象。该处理方式将细节进行封装,仅导出必要的功能。

从模块里导出对象,ES6为我们提供了不同方法,见下面的讨论。

内联导出

ES6模块里的对象可在创建它们的声明中导出。一个模块中可无数次使用export,所有的对象将被一起导出。请看下面的例子:

export class Employee{  constructor(id, name, dob){    this.id = id;    this.name=name;    this.dob= dob;  }  getAge(){    return (new Date()).getYear() - this.dob.getYear();  }}export function getEmployee(id, name, dob){  return new Employee(id, name, dob);}var emp = new Employee(1, "Rina", new Date(1987, 1, 22));

案例中的模块导出了两个对象: Employee类,getEmployee函数。因对象emp未被导出,所以其仍为模块私有。

导出一组对象

尽管内联导出很有效,但在大规模模块中,它就很难发挥作用了,因为我们可能无法追踪到模块导出来的对象。在这种情况下,更好的办法是,在模块的末尾单独进行导出声明,以导出该模块中的全部对象。

使用单独导出声明重写上一案例中的模块,结果如下:

class Employee{  constructor(id, name, dob){    this.id = id;    this.name=name;    this.dob= dob;  }  getAge(){    return (new Date()).getYear() - this.dob.getYear();  }}function getEmployee(id, name, dob){  return new Employee(id, name, dob);}var x = new Employee(1, "Rina", new Date(1987, 1, 22));export {Employee, getEmployee};

在导出时,重命名对象也是可以的。如下例所示,Employee在导出时名字改为了Associate,函数GetEmployee改名为getAssociate。

export {    Associate as Employee,    getAssociate as getEmployee  };

Default导出

使用关键字default,可将对象标注为default对象导出。default关键字在每一个模块中只能使用一次。它既可以用于内联导出,也可以用于一组对象导出声明中。

下面案例展示了在组导出语句中使用default:

export default {    Employee,    getEmployee};

导入模块

现有模块可以使用关键字import导入到其它模块。一个模块可以被导入任意数量的模块中。下文展示了导入模块的不同方式。

无对象导入

如果模块包含一些逻辑要执行,且不会导出任何对象,此类对象也可以被导入到另一模块中。如下面案例所示:

import './module1.js';

 导入默认对象

采用Default导出方式导出对象,该对象在import声明中将直接被分配给某个引用,如下例中的“d”。

import d from './module1.js';

 导入命名的对象

正如以上讨论的,一个模块可以导出许多命名对象。如果另一模块想导入这些命名对象,需要在导入声明中一一列出这些对象。举个例子:

import {Employee, getEmployee} from './module1.js';

 当然也可在同一个声明中导入默认对象和命名对象。这种情况下,默认对象必须定义一个别名,如下例。

import {default as d, Employee} from './module1.js';

 导入所有对象

以上几种情况,只有import声明中列举的对象才会被导入并被使用,而其它对象则无法在导入模块中使用。当然,这就要求用户了解哪些对象可以导出并加以利用。如果模块导出大量对象,另一模块想引入所有导出的对象,就必须使用如下声明:

import * as allFromModule1 from './module1.js';

 allFromModule1这一别名将指向所有从module1导出的对象。在导入模块中,它们作为属性可被访问。

可编程式的按需导入

如果想基于某些条件或等某个事件发生后再加载需要的模块,可通过使用加载模块的可编程API(programmatic API)来实现。使用System.import方法,可按程序设定加载模块。这是一个异步的方法,并返回Promise。

该方法的语法示例如下:

System.import('./module1.js')    .then(function(module1){        //use module1    }, function(e){        //handle error    });

如果模块加载成功且将导出的模块成功传递给回调函数,Promise将会通过。如果模块名称有误或由于网络延迟等原因导致模块加载失败,Promise将会失败。

ES6模块使用现状

到目前为止,所有浏览器还不能自然支持ES6模块,所以在浏览器加载之前,我们需要使用转译器(transpiler)将代码转换成ES5。直到现在,我一直使用Traceur作为我的转译器,建议大家使用相同的工具将模块代码转化为浏览器可识别的代码。让我们看看编译ES6模块的几种不同的方法。

使用Traceur动态编译ES6模块

当浏览器加载脚本后,我们可以使用Traceur的客户端库动态编译ES6模块。使用该方法,运行模块无需运行任何命令。我们要做得就是,在页面上加载Traceur库,及添加代码脚本来运行WebPageTranscoder。

<script>    new traceur.WebPageTranscoder(document.location.href).run();</script>

现在,我们就可以在script标签内,将类型指定成模块,以此导入任何一个ES6文件。

<script type="module">    import './modules/import1.js';</script>

类型指定为模块的任何脚本标签将被ES6客户端库获取并处理。上面代码块中的导入语句将发送AJAX请求,捕获相应的JavaScript文件,并载入它。如果模块内部引用了另一个模块,单独的AJAX请求将发出,以加载与引用模块相对应的文件。

使用Traceur命令编译ES6模块

使用Traceur命令可以将ES6模块编译成AMD或者CommonJS模块。这个方法有两大优点。

  1. 模块完成编译,浏览器不必执行额外动作;
  2. 如果应用已经使用ES5及AMD(或CommonJs)模块系统构建了一半,程序的另一半也可以使用ES6,并被编译为这些模块系统中的任何一个,而不是立即把整个应用编译成ES6。

为了使用编译完成的AMD/CommonJs的模块,我们需要包含支持模块系统的库。我个人比较倾向AMD,所以我将在这里介绍一下它。CommonJS模块的步骤和这个差不多。

下面这句命令是用来让包含ES6模块的文件夹编译成AMD,并把它们存储在一个单独的文件夹:

traceur --dir modules es5Modules --modules=amd

使用CommonJs,你需要在上面命令中使用commonjs代替modules。

在这里,modules指的是包含ES6的文件夹,es5Modules指的是输出目录。如果查看es5Modules文件夹下的任何文件,你将看到该AMD定义块。require.js支持AMD,所以我们可以在HTML页面中,使用script引入require.js,并用data-main属性指明开始文件,就像下面这样:

<script src="bower_components/requirejs/require.js" data-main="es5Modules/import2.js"></script>

使用Traceur Grunt Task编译ES6模块

使用命令编译模块很累而且更容易出错。我们可以使用grunt-traceur自动化编译过程。此时,你需要安装NPM包。

npm intall grunt-traceur –save

Grunt任务所需数据与提供给命令的数据一样。下面是任务的一些配置:

traceur: {  options: {    modules: "amd"  },  custom: {    files: [{      expand: true,      cwd: 'modules',      src: ['*.js'],      dest: 'es5Modules'    }]  }}

现在你可以在控制台里使用下面的命令来运行上面的Grunt任务:

grunt traceur

正如你所看见的那样,它和我们使用命令所产生的效果是一样的。

结论

任何一个大型应用中,模块化十分必要。ES6模块为JavaScript提供了该特性,这些模块提供了众多选择来导出和引入对象。我很期待该特性被浏览器支持的那一天,到时我们无需加载任何第三方库即可创建、加载JavaScript模块。目前流行的客户端MV*框架Angular.js在其2.0版本(目前还在开发中)中就使用了ES6的模块化。

让我们开始使用模块系统,从而让我们的代码更具组织和可读性。(编译:肖元州,审校:陈秋歌)

原文链接:Modules in ECMAScript 6 (ES6)

最火HTML5 JavaScript游戏引擎:国外篇(二) 挑战最强大脑——来自全球的14个编码社区 摩尔定律让位于贝索斯定律 InPaaS:新一代云计算应用平台 【技术博客推荐】樊哲谈机器学习算法与Mahout Scala 2.11.0发布 仅支持Java 8部分功能 诺基亚彻底“卖身” 变“微软移动” 10款高效简化移动开发过程的工具 《近匠》AppCan:打造成云生态的移动PaaS平台 WhatsApp壮举:活跃用户超五亿、日7亿幅照片和1亿个视频 如期而至,GCC 4.9.0正式版发布! 【走近讲师】孙昌勋:容联云通讯构筑通讯能力云平台生态圈 最新版本的Hive 0.13发布,增加ACID特性 开发中的“软”与“硬”:高画质移动游戏开发之道 从国际编码竞赛来看编程语言受众度 AMD迎接变革:加速OpenCL的未来 角逐Hadoop市场,Cloudera与Hortonworks竞争白热化 OpenStack将提供对Docker的支持 Facebook Q1财报:营收25.02亿美元、59%来自于移动 黑客们盯上了卫星,IOActive的惊人发现 开发汽车应用之前,你必须了解的7件事 埃森哲:智能电视潮流,势不可挡 直接拿来用!Facebook移动开源项目大合集 高大上Hadoop技术培训:Hadoop在企业中的应用实战 【走近讲师】新浪丛磊:通过MAE构建企业内部私有PaaS平台 API增长新趋势 消息应用类独占鳌头 软件项目为何会失败? 严澜:搭建高可用的MongoDB集群(二) 微软、思科、Google、Facebook等联合推出CII 狙击“Heartbleed” 这是个神奇的国度:细数五大印度裔IT牛人CEO Atmel CMO Sander Arts眼中的创客运动 我用Server.Transfer来做两个页面之间传值,我怎么让页面成为新窗口打开呀,100分 高分请教XML高手 在InterDev中,我什么说我不能连接web server 请问 怎么响应一个按钮打开一个新页面,同时关闭旧页面 各位PM进来谈谈自己的经历和经验教训。 picturebox控件问题 jdbc的奇怪限制,不能reread row data UNIX支持中文否,请推荐UNIX好书 高分求助:怎样发布sapi.dll 我发现我变笨了!!或许我本来就很笨。但是问句实在的:IT会使人变笨吗? hal.dll文件丢失!winxp修复的问题!急! pb如何连接execl 用Windows2000Internet连接共享出现的问题,请各位大侠解决一下! 关于数据库字段大小问题 ★★如何给一个Dialog加背景图片?★★ MSDN Library 2003中文版3CD下载 我用access+adoquery+DBgrid,我在拖动改变dbgrid的列宽时,为何会报错? 关于软件汉化的问题。 请教:可以从硬盘上直接安装linux吗? C语言二级考什么题型 MYSQL是不是不能建视图啊?新手清多多指教 还是ASP与SQL联不上的问题,在线等待 请问:C:\Documents and Settings\user\Local Settings\Temporary Internet Files里的内容怎么删除 紧急求助!sqlserver服务启动不了 怎么知道当前这台UNIX的IP地址 getWidth怎么用呀??????????????? 我的是win2000 server ,怎么会老出现什么explorer.exe错误???? 申请免费空间 请大家谈makefile的写法 请大家帮忙 求助,Windows2003启动时提示 \windows\system32\config\system文件丢失或已损坏, 无法启动。 怎么样使程序调试结果在屏幕上停留时间长点 初学,请问Edit如何添加到Memo中? 如何制作安装程序? ******请问一下vs.net2002与vs.net2003是什么区别???***** 求助高手——怎样读取BIOS中的信息? 找个DELPHI的中文免费打包程?? 求书 正在填写“企业现有信息系统开发环境情况表”,上面有一栏“系统分析与设计工具”应填什么? 哪里有ASP.Net中在Combox下拉Tree的控件……?????急,急 菜鸟问题,仍然在线结帐,请进 谁能告诉我怎么配置环境和怎么运行c#的程序啊 关于WebBrowser的问题,请教 请教一个javascript的参数传递 请问,怎样让弹出式页面目录上的链接关闭本页的同时,在上一级页面上显示新网页内容? 水晶报表在webform的登陆出错问题,急啊!!!! pb下如何对syabse数据库进行备份,在线等待,各位大哥,小弟先谢过了 小资是怎样的一群人 (zt) 菜鸟问题 JSP+JavaBean社区BBS-CS天乙社区V4.0发布安装包 升级到4.2.3版本出现的一些问题。受打击! 英语翻译1,our work is informed by the conflicts of today and those of the past,2,we try to embrace conflict in our work and make those exposed to it confront it GMAT数学满分是多少 我准备考美国的工商管理,需要考GMAT,请问数学方面怎样才能拿满分呢? 怎样得GMAT数学满分? GMAT想考高分,是不是数学都要得满分啊?一般怎么复习捏? DS_300B,警告 Extended Fabric license is expired我的SAN交换机DS_300B,最近有如下警告信息:2012/10/29-05:00:00,[SEC-1114],8333,CHASSIS,WARNING,DS_300B,JSYZBBtYGmK79tYgtAJYt4KFWtMATC3tXEXrt9HmLJFN4AEM9G [ Extended Fabric license ] is exp License expired on 关于刷机问题,HTC T328W 怎么刷机,求全部刷机过程.HTC T328W怎么刷机,求全部刷机过程. 修改病句:开展“体艺二加一”活动可以丰富和提高大家的课余生活.和提高删掉,就改变句意,可不可以改成提高什么? 我们班开了一次交流课余生活的主题活动(病句改) 用12根相同的火柴棍摆成长方形或正方形一你有几种不同的摆法二,你摆成的图形面积有变化么?周长呢? 浙江释疑吴英资产处置:困难重重 已追托雷斯被\"激活\"进绝杀球 穆里尼永康车王非法集资近10亿判无期 曾坐前三季全国物流总额145.7万亿元 几内亚总统为中国房产开发项目奠基国际新闻早报:俄罗斯外长说俄乌贸易关中国核潜艇露面,战略威慑力更强“金发女孩”父母否认卖女儿 因没钱放阿联酋中国商会为中资企业办讲座澳媒评论称中国买家推高悉尼房价四分之11年来最强风暴袭英国 海陆空交通大有特色还要强品牌现职公务员不得兼任负责人微信造谣 行拘罚款习近平会见约旦国王阿卜杜拉二世利用地源热泵装置电价下调“三大引擎”助推宿州农业转型升级名校学子感受中国文化抱团拓市场 焦枣出深山骆岗机场内发生火灾合肥西二环合淮路将建互通立交扎根深山点“心灯”
备案号:鲁ICP备13029499号-2 说三道四 www.s3d4.cn 说三道四技术文摘