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

全面解析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)

Java 9中新的货币API 唯“简”不破,Apple Watch App的设计之道 Google Glass从X labs“毕业”,更换项目负责人 不进化,则消亡——互联网时代企业管理的十项实践 RedMonk 2015年1月编程语言排行榜:进击的Swift! 微信开放JS SDK,这场web巨变意味着什么? 盘点一些iOS开发技巧:Swift和Objective-C 2014年,那些“颜值”爆表的UI动画 《近匠》爱加密:谁说iOS应用不需要保护? SwiftColorArt:开源易用的Swift图片类库 人气爆棚 干货满满 中国电信天翼开放平台开放日圆满落幕 必须Mark!43个优秀的Swift开源项目推荐 治拖延、疗懒癌!安利12款提高工作效率工具 把ES6带进Node社区 Io.js 1.0.0正式发布 Java 2014:10个最热门、最具争议性的话题 回顾:2014年最流行前端开发框架对比评测 实例讲解SQL注入攻击 调试大规模服务器集群的五大策略 .NET编译平台Rosly将迁移到Github Wasai虚拟现实头盔与体验馆发布:虚拟现实的线下经济 订票系统不再瘫痪 阿里云确认与12306合作 Apple前工程师Warren Moore:Swift中Metal使用初体验 手把手教学:详解Swift中的iOS设计模式 见微知著,那些触动人心的应用细节设计(一) 设计优秀API七大要诀 需求 细节 BI项目成功的五大要素 BI项目成功的五大要诀 最受程序员欢迎的公司榜单发布:2015 IT人才招聘趋势分析 Swift中的指针操作及使用 Android性能优化典范 专访Google涂鸦首席设计师Ryan Germick:为什么世界需要Doodle? 为啥我的头发长的这样慢!!! ■■如何做可媲美Realplayer,WindowsMediaPlayer的Internet收音机(黄金网络收音机),N多中文电台,大小仅292K??? 如何控制系统工具栏的显示与隐藏 我是否要改行!现在的待遇很好,可是…… 联想的机子伤透我的心!高分求教!!!!!! 但愿有人能给出答案。 如何验证邮件地址的有效性? 一个简单的问题 请教azureeW 、wudaqiang、net_star、ltz 、west_wood——NetMeeting SDK的相关问题 关于vb中多文档编程的问题 请高手指点迷经!怎么得到系统硬件信息。 高分求救:我的2000server经常出现"HTTP/1.1 新建会话失败",应如何解决? MFC 初学者的一个问题? 谢谢! 高分求救:我的2000server经常出现"HTTP/1.1 新建会话失败",应如何解决? win2000server重装后,硬盘丢失,盼解决。 想认识西安的 Delphier,报上你的 QQ 和 Email? MFC 初学者的一个问题? 谢谢! 应该很简单吧:如何在程序中使用外部Dll及Lib? 简单的问题 急 送分 MYSQL权限问题~!~!小菜提问. 使用MSCOMM从串口中读出乱码的问题 如何 实现 数据 窗口 对象 的 不同 行显示不同颜色 SELECT * FROM mytable order by IndexNum where (IndexNum%100=0) , 那里有问题? 强烈建议CSDN论坛加入贴图功能!! PB做的客户端进销存软件的毕业论文,该怎么写,给点意见! 招聘网页美工人员和ASP程序员 [帮我顶]强烈建议CSDN论坛加入贴图功能!! 请问如何屏蔽其他窗口? filter 问题 ????????????? 国内哪个大学的计算机图形学最强? 如何用ACCESS建立一个合理的数据库? 需要空间的看看 如果主键和其他表的外键相连是不是没有办法删掉其中某些表的数据吗? 求助:xls用odbc不能编辑,出错:Syntax error or access violation PE格式的资料你有吗? VB 中的类模块与模块之间的联系是什么? 怎样从windows访问linux的ext3分区? 关于CFM-Resources免费空间(asp+cf+access+30M)的问题!100分 消息接收后拆分,毕设急!!60分已经是倾我所有了 热心的VC大虾,请多帮忙 请问如何用JAVASCRIPT获取服务器时间,并以字符串形式输入,如:200206021943,谢谢! ADO访问access数据库,怎样删除一条记录???? 关于字符串转日期型的值的问题 Tadoquery中参数返回的问题 请问各位高手门,现在公司的数据库编程普遍用哪种模式ADO或ODBC? 如何在CFormView中添加位图背景?? 谁来帮我看看这几行程序!我实在无法解决,谢谢! 请问如何自己编程使用Logitech QuickCam 人工智能的问题(乜人都要睇) 400分题目问题要解决了就立刻、马上、立即、给分! 菜鸟问题c++中清屏函数在哪? 石钟乳的意思?啥意思?简洁些 凸面镜和凹面镜的成象原理凸面镜会将光发散,我们看到的凸面镜中的成像就应是这些发散光的反向延长线汇聚成的缩小的虚象,但为何都说是成放大的像呢?到底是什么呀? 哪位大神给做个Burning(燃烧)(燃烧)歌曲链接要空间可用的哦~ 石钟乳在字典里是什么意思新华字典,第几个? 凸面镜和凹面镜成像的原理和成像的特点请详细一点,不要答非所问,不要答非所问!OK? 一年级语文造句 太阳为什么能在宇宙中燃烧 质点作变速直线运动,其唯一函数为s(t)=t+1/t,求其速度函数v(t)和加速度函数a(t) 某导体的横截面积为2毫米2,5分钟内通过这导体的电量300库伦,则导体中的电流大小给个过程 宇宙里太阳为什么能燃烧.根据我理解 没有氧气的话是什么东西都不能燃烧的,在整个太空上应该都没有氧气,为什么它能够自燃,还有就算它是核聚变也不可能是这么多吧,能永远?核聚变应该是 肥皂的成分和功效1.成分2功效 2安= 库、秒 如果通过导体的电流是2安,则一分钟通过导体横截面的电量是 库伦?怎样通过电流表的指针判断电流表的量程?2安= 库、秒 如果通过导体的电流是2安,则一分钟通过导体横截面的 高锰酸钾和过氧化钠的反应高锰酸钾和过氧化钠会不会反应?那在加水呢?反应放热吗? 一个肥皂盒里面有多少个肥皂我的意思是一个肥皂包装纸箱里面有多少块肥皂(牌子是立白强效去渍) 一段导体10秒内通过6库伦的电量,通过这段导体的电流? 人们发现什么和什么能把光聚集起来,形成强光和高温? 可不可以用香皂盒肥皂洗脸? 如果通过导体的电流是2安,则1分钟内通过导体横截面的电量是_____库伦? 人们发现( )和( )能把光线汇集起来,形成强光和高温 一种肥皂,他的长、宽、高分别是16厘米,6厘米,3厘米,一想装30条肥皂,设计一种包装箱,符合下列要求(1)肥皂装箱时,面积相同的面要互相对接.(2)包装箱是一个长方体.(3)装入肥皂后不留 拆汽车电瓶时,为什么先拆负极,装的时候先装正极啊? 汽车级玻璃与建筑级玻璃哪个对平板玻璃的质量要求更严格一些从哪方面比较,肯请指教... 风像什么造句一年级语文 每节电池都有正极和负极 怎样形成强光和获的高温? 眼睛弄到甲醇怎么办 自动喷淋恒压供水设备和加压泵有什么区别 火焰能在宇宙中燃烧吗?还是只能在宇宙爆炸中燃烧?如果能燃烧,是为什么 忘记.记得.造句 什么是螺杆式喷射吸水泵,重点求教螺杆式是什么意思,这种工作方式的吸水泵跟别的吸水泵有什么不同? 谁有可以放在空间里的Burning这首歌的歌曲链接.我需要的是 Maria Arredondo 的.如果有的请告诉我. 物体的运动方程为S=四分之一t的三次方减3,则t=5时的瞬时速度 静摩擦力大小一般是怎么算出来的? 对光线会起会聚作用的有凸透镜和( )镜如题、除了凸透镜会对光线起会聚作用、还有什么镜可以? 一物体的运动方程是s=t^3+10,求该物体在t=3时的瞬时速度 什么会导致眼睛失明 凸透镜只对平行于主轴的光线起会聚作用这句话对不对? 充电时,电池负极都与电源负极连,正极都与电源正极连? 如何计算静摩擦力的大小 凸透镜对光线有会聚作用,那么光通过凸透镜后都能会聚于一点吗? 电池(直流电电源)为什么要正极接负极如题,电池电源为什么要把一个电池的正极接另一个电池的负极,这样为什么不会短路?交流电里面为什么不可以?这样的串联和并联有什么不同?我是说 甲醇有毒 误饮可使眼睛失明可以治疗吗? 电饭锅电阻很大吗 凸透镜对于任何光线都有会聚作用吗? 汽车电源开关是关正极还是负极 美的电饭锅电源像电阻的东西是什么?可以直接短接吗?或者我上个电阻可以吗?美的电饭锅电源像电阻的东西是什么?可以直接短接吗?或者我上个电阻可以吗?那个已经坏了 太空中燃烧会不会产生烟雾?地球上燃烧产生烟雾的物质在太空中燃烧会不会产生烟雾呢? 玩具车的塑料履带是什么机器加工的? 怎样用甲醇萃取活性炭中的苯? 河北有卖皮革和海绵黏在一起的布料,就是人造革下面有层薄薄的海绵 有联系电话吗?我用来做椅子和摩托车座 请问有谁知道如何用塑料瓶做玩具小汽车? 硫化氢属于有机气体吗?吸附硫化氢的废活性炭有毒吗?如何处置? 小车布料坐位上有呕吐物怎样清除 [来源:www.efchan.com ]公仔玩具一般是什么塑料 静摩擦力 动摩擦力当推力大于最大静摩擦力时,物体被推动,然后力可以稍稍减小推力不是一定要小于最大静摩擦力的吗?小于的话不就静止了这就成了:推力大于最大静摩擦力,不可能.推力小 请问为什么点燃氢气和天然气的混合体不会引起爆炸? 遥控玩具车的遥控器上的那根软的塑料天线是用什么塑料材料制成的? 静摩擦力与什么有关?动摩擦力呢? 物体做匀变速直线运动,其位移随时间变化的函数关系式为s=4t+2t2(m),则物体的初速度和加速度分别是()A.0 4m/s2 B.4m/s 2m/s2C.4m/s 1m/s2 D.4m/s 4m/s2 甲醇滴到眼睛里会导致双目失明吗?我只知道甲醇是通过误服导致分解甲酸致使双目失明的.如果将甲醇溶液直接滴到眼睛里会不会导致失明?别写大量的什么分子式、机理、用途等无用的东西
备案号:鲁ICP备13029499号-2 说三道四 www.s3d4.cn 说三道四技术文摘