`

全面解析ECMAScript 6模块系统

 
阅读更多

转自:http://www.iteye.com/news/30508

 

在任何一个大型应用中模块化是很常见的。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模块系统启发于上述现有模块系统,它具有以下特性: 

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


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

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

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

内联导出 

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

Js代码 
  1. export class Employee{  
  2.   constructor(id, name, dob){  
  3.     this.id = id;  
  4.     this.name=name;  
  5.     this.dob= dob;  
  6.   }  
  7.   getAge(){  
  8.     return (new Date()).getYear() - this.dob.getYear();  
  9.   }  
  10. }  
  11. export function getEmployee(id, name, dob){  
  12.   return new Employee(id, name, dob);  
  13. }  
  14. var emp = new Employee(1, "Rina"new Date(1987, 1, 22));  


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

导出一组对象 

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

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

Js代码 
  1. class Employee{  
  2.   constructor(id, name, dob){  
  3.     this.id = id;  
  4.     this.name=name;  
  5.     this.dob= dob;  
  6.   }  
  7.   getAge(){  
  8.     return (new Date()).getYear() - this.dob.getYear();  
  9.   }  
  10. }  
  11. function getEmployee(id, name, dob){  
  12.   return new Employee(id, name, dob);  
  13. }  
  14. var x = new Employee(1, "Rina"new Date(1987, 1, 22));  
  15. export {Employee, getEmployee};  


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

Js代码 
  1. export {  
  2.     Associate as Employee,  
  3.     getAssociate as getEmployee  
  4.   };  


Default导出 

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

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

Js代码 
  1. export default {  
  2.     Employee,  
  3.     getEmployee  
  4. };  


导入模块 

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

无对象导入 

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

Js代码 
  1. import './module1.js';  


导入默认对象 

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

Js代码 
  1. import d from './module1.js';  


导入命名的对象 

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

Js代码 
  1. import {Employee, getEmployee} from './module1.js';  


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

Js代码 
  1. import {default as d, Employee} from './module1.js';  


导入所有对象 

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

Js代码 
  1. import * as allFromModule1 from './module1.js';  


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

可编程式的按需导入 

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

该方法的语法示例如下: 

Js代码 
  1. System.import('./module1.js')  
  2.     .then(function(module1){  
  3.         //use module1  
  4.     }, function(e){  
  5.         //handle error  
  6.     });  


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

ES6模块使用现状 

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

使用Traceur动态编译ES6模块 

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

Js代码 
  1. <script>  
  2.     new traceur.WebPageTranscoder(document.location.href).run();  
  3. </script>  


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

Js代码 
  1. <script type="module">  
  2.     import './modules/import1.js';  
  3. </script>  


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

使用Traceur命令编译ES6模块 

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

1.模块完成编译,浏览器不必执行额外动作; 
2.如果应用已经使用ES5及AMD(或CommonJs)模块系统构建了一半,程序的另一半也可以使用ES6,并被编译为这些模块系统中的任何一个,而不是立即把整个应用编译成ES6。 
为了使用编译完成的AMD/CommonJs的模块,我们需要包含支持模块系统的库。我个人比较倾向AMD,所以我将在这里介绍一下它。CommonJS模块的步骤和这个差不多。 

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

Js代码 
  1. traceur --dir modules es5Modules --modules=amd  


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

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

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


使用Traceur Grunt Task编译ES6模块 

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

Js代码 
  1. npm intall grunt-traceur –save  


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

Js代码 
  1. traceur: {  
  2.   options: {  
  3.     modules: "amd"  
  4.   },  
  5.   custom: {  
  6.     files: [{  
  7.       expand: true,  
  8.       cwd: 'modules',  
  9.       src: ['*.js'],  
  10.       dest: 'es5Modules'  
  11.     }]  
  12.   }  
  13. }  


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

Js代码 
  1. grunt traceur  


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

结论 

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

让我们开始使用模块系统,从而让我们的代码更具组织和可读性。

分享到:
评论

相关推荐

    全面解析ECMAScript6模块系统

    摘要:模块化对任何大型...RaviKiran在《ModulesinECMAScript6(ES6)》一文中主要讨论了ES6模块系统。以下为该文章的简译内容:无论使用何种编程语言开发大型应用,最关键的特性就是代码模块化。这个概念在不同的编程语

    深度解析 ECMAScript 6

    **ECMAScript 6(简称ES6)是JavaScript语言的一个重大升级版本,它引入了许多新的特性和语法糖,极大地提升了开发效率和代码的...阅读《深度解析 ECMAScript 6》这本书,将有助于你全面深入地理解和运用这些特性。

    JavaScript 语言教程 全面介绍 ECMAScript 6 新增的语法特性包括语言程序库数据结构算法 系统 网络 zip

    随着ECMAScript 6(简称ES6)的发布,JavaScript获得了许多新的语法特性,极大地提升了开发效率和代码可读性。本教程将全面深入地探讨这些特性,并结合数据结构、算法、系统以及网络知识,为开发者提供一个全面的...

    JavaScript权威指南-ECMAScript5.pdf

    15. **模块化**:ES6引入了模块系统,使用`import`和`export`关键字来导入和导出模块,解决了大型项目中的代码组织和重用问题。 以上知识点仅是JavaScript众多特性和应用的一小部分,《JavaScript权威指南-...

    ECMAScript 6 全套学习目录 整理 完结

    ECMAScript 6,简称ES6,是JavaScript语言的一个重要版本更新,引入了大量的新特性,极大地提升了开发效率和代码质量。以下是对ES6主要特性的详细解析: 1. **变量声明**: - `let` 和 `const`:在ES6中,新增了`...

    Web前端模块化开发教程全套ppt课件最全电子讲义完整版教学教程整套全书课件ppt.pptx

    "Web前端模块化开发教程全套ppt课件最全电子讲义完整版教学教程整套全书课件ppt.pptx" 本资源是一个关于Web前端模块化开发的教程,全套ppt课件包括了从ES6基础到模块化开发的各个方面的知识点。下面是从标题、描述...

    exploring-es6源码解析

    《探索ES6源码解析》是一本深入剖析ECMAScript 6(简称ES6)语言特性的技术书籍,旨在帮助开发者全面理解并掌握这门现代JavaScript的核心内容。ES6,也被称为ECMAScript 2015,是JavaScript语言的一个重大升级版本,...

    2020Web前端高频面试题全解析.pdf

    "2020Web前端高频面试题全解析" 一、JavaScript基础 1. IIFE(Immediately Invoked Function Expression)是JavaScript中的一种设计模式,用于创建自执行匿名函数。其基本形式为`(function(){// code.. })()`,它...

    李炎恢JavaScript全套课程讲义

    **JavaScript全方位解析** JavaScript,简称JS,是一种广泛应用于网页和网络应用的脚本语言,尤其在前端开发领域占据着核心地位。它与HTML和CSS一起,构成了网页开发的三驾马车,使得网页具备交互性、动态性和实时...

    管理系统系列-- 全面ESM+Vue3+Vite+Element-Plus+TypeScript编写的一款后台管理系.zip

    这篇文档将深入解析标题所提及的“管理系统系列”,它是一个基于现代前端技术栈构建的后台管理系统,主要采用了ESM(ECMAScript Modules)、Vue3、Vite、Element-Plus和TypeScript等技术。我们将逐一探讨这些技术的...

    Node.js-Acorn-一个微小快速的JavaScript解析器完全用JavaScript编写

    1. **全功能**: Acorn支持ECMAScript 2019及之前的全部语法特性,包括ES6的箭头函数、模板字符串、类、模块等。 2. **模块化**: Acorn通过CommonJS模块系统导出,可以方便地与其他Node.js模块集成。 3. **小型化**: ...

    在线移动书城管理系统-3期(AL027) AL027020100001 软件需求说明书.doc

    总结,这份软件需求说明书详细列出了在线移动书城管理系统第三期的开发需求,包括系统的功能模块、用户交互和数据交换机制,为开发人员提供了清晰的开发蓝图,确保了系统功能的全面性和用户体验的优质性。

    Firefox火狐浏览器官方45.0b6-win32版本exe在线安装包

    1. 支持ES6(ECMAScript 6):这是一个对JavaScript语言的重大更新,增加了许多新的语法特性,如类、模块、箭头函数等,提升了开发效率。 2. 多进程架构优化:为了提高性能和稳定性,Firefox开始引入Electrolysis...

    Meriyah一个100兼容自托管的Javascript解析器高度关注性能和稳定性

    5. **模块化**:Meriyah支持CommonJS和ES模块,使得它在现代JavaScript项目中易于导入和使用。 6. **错误处理**:Meriyah提供了详细的错误报告,有助于开发者快速定位并修复语法错误,这对于调试和维护代码尤其有用...

    es6标准入门(第三版)

    《ES6标准入门(第三版)》是阮一峰先生撰写的一本深入解析ECMAScript 6(简称ES6)的权威指南。这本书全面、详细地介绍了ES6的新特性和用法,旨在帮助开发者掌握这一JavaScript语言的重要更新,提升编程效率和代码...

    es-module-lexer:低开销的词法分析器专用于ES模块解析,可进行快速分析

    一个很小的单个JS文件(已压缩4KiB),其中包括内联Web程序集,仅用于ECMAScript模块语法的非常快速的源代码分析。 以性能为例,与最快的JS解析器Acorn相比,Angular 1(720KiB)只需100毫秒即可在5毫秒内完全解析...

    JavaScript权威指南(第6版)+源代码

    《JavaScript权威指南(第6版)》是一本深入解析JavaScript编程语言的经典著作,深受广大开发者喜爱。这本书详尽地介绍了JavaScript的核心概念、语法特性和实际应用,旨在帮助读者全面掌握这门广泛应用于Web开发的语言...

    基于ASP的微博系统源码 v1.0.zip

    6. 数据库设计:微博系统的背后通常有一个精心设计的关系型数据库,如MySQL或Access,用于存储用户信息、微博内容、评论、关注关系等数据。 【ASP编程基础】 在ASP中,常用的语言有VBScript和JScript,它们都是...

    You Don't Know JS(中英全6本,带目录)

    6. **《ES6 & Beyond》**:最后,本书关注了ECMAScript 6及其后续版本的新特性和最佳实践。这部分内容涵盖了模块系统、类、解构赋值、生成器、迭代器、尾调用优化等,帮助读者掌握现代JavaScript开发的必备知识。 ...

    基于ASP的音像销售系统设计与实现毕业设计(源代码+项目报告).zip

    这个毕业设计涵盖了从需求分析到系统实现的全过程,旨在让学生掌握Web应用开发的基本技能。 【描述解析】 描述中的内容与标题相同,表明这是一个包含源代码和项目报告的完整项目资源。源代码是实际的程序实现,而...

Global site tag (gtag.js) - Google Analytics