快速使用Romanysoft LAB的技术实现 HTML 开发Mac OS App,并销售到苹果应用商店中。
 
《HTML开发Mac OS App 视频教程》
 
官方QQ群:(申请加入,说是我推荐的
  • App实践出真知 434558944       
  • App学习交流 452180823          
 

在任何一个大型应用中模块化是很常见的。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的模块化。

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

全面解析ECMAScript 6模块系统的更多相关文章

  1. 使用ECMAScript 6 模块封装代码

    JavaScript 用"共享一切"的方法加载代码,这是该语言中最容易出错且最容易让人感到困惑的地方.其他语言使用诸如包这样的概念来定义代码作用域,但在 ECMAScript 6 ...

  2. ABP(现代ASP.NET样板开发框架)系列之4、ABP模块系统

    点这里进入ABP系列文章总目录 基于DDD的现代ASP.NET开发框架--ABP系列之4.ABP模块系统 ABP是“ASP.NET Boilerplate Project (ASP.NET样板项目)” ...

  3. ABP框架 - 模块系统

    文档目录 本节内容: 简介 模块定义 生命周期方法 PreInitialize(预初始化) Initialize(初始化) PostInitialize(提交初始化) Shutdown(关闭) 模块依 ...

  4. Node.js 教程 04 - 模块系统

    前言: Node.js的模块系统类似于C/C++的文件引用,可以声明对象,也可以定义类 创建对象. 大家这么理解,就简单了. 定义: 为了让Node.js的文件可以相互调用,Node.js提供了一个简 ...

  5. nodejs的模块系统(实例分析exprots和module.exprots)

    前言:工欲善其事,必先利其器.模块系统是nodejs组织管理代码的利器也是调用第三方代码的途径,本文将详细讲解nodejs的模块系统.在文章最后实例分析一下exprots和module.exprots ...

  6. ECMAScript 6 模块简介

    任何平台的其中一个重要特性,除了需要支持代码库外就是模块.直到现在,Javascript还不支持原生的模块化.结果是,各种解决方案都将模块添加到类库中,比如CommonJS modules(部分由no ...

  7. ABP模块系统

    ABP模块系统 基于DDD的现代ASP.NET开发框架--ABP系列之4.ABP模块系统 ABP是“ASP.NET Boilerplate Project (ASP.NET样板项目)”的简称. ABP ...

  8. Node.js 模块系统

    为了让Node.js的文件可以相互调用,Node.js提供了一个简单的模块系统. 模块是Node.js 应用程序的基本组成部分,文件和模块是一一对应的.换言之,一个 Node.js 文件就是一个模块, ...

  9. 前端笔记之NodeJS(二)路由&REPL&模块系统&npm

    一.路由机制(静态资源文件处理) 1.1 Nodejs没有根目录 MIME类型:http://www.w3school.com.cn/media/media_mimeref.asp 在Apache中, ...

随机推荐

  1. 前端自动化之路之gulp,node.js

    随着现在前端技术的不断发展,和各个公司对前端项目开发更新速度的要求,前端自动化越来越受到大家的重视,之前传统的前端开发方式已经越来越不能满足开发的需求了,于是各种自动化工具随之产生了.而gulp就是其 ...

  2. C++闭包,一样很简单

    引用百度上对闭包的定义:闭包是指可以包含自由(未绑定到特定对象)变量的代码块:这些变量不是在这个代码块内或者任何全局上下文中定义的,而是在定义代码块的环境中定义(局部变量).“闭包” 一词来源于以下两 ...

  3. Lucene分词报错:”TokenStream contract violation: close() call missing”

    Lucene使用IKAnalyzer分词时报错:”TokenStream contract violation: close() call missing”  解决办法是每次完成后必须调用关闭方法. ...

  4. lzugis——Arcgis Server for JavaScript API在自己的定义InfoWindow

    你看到这个标题嫌烦.因为我最近一直与研究问题,相关文章使这些也可以只,同时要读我文章的朋友.我的文章能够给你带来帮助. 在相关的内部的前两篇文章,达到InfoWindow经div实现的东西,成Info ...

  5. UVa 12657 Boxes in a Line(应用双链表)

    Boxes in a Line You have n boxes in a line on the table numbered 1 . . . n from left to right. Your ...

  6. Etag & If-None-Match 专题

    一.概述 缓存通俗点讲,就是将已经得到的‘东东’存放在一个相对于自己而言,尽可能近的地方,以便下次需要时,不会再二笔地跑到起始点(很远的地方)去获取,而是就近解决,从而缩短时间和节约金钱(坐车要钱嘛) ...

  7. Android实现图片滚动控件,含页签功能,让你的应用像淘宝一样炫起来

    首先题外话,今天早上起床的时候,手滑一下把我的手机甩了出去,结果陪伴我两年半的摩托罗拉里程碑一代就这么安息了,于是我今天决定怒更一记,纪念我死去的爱机. 如果你是网购达人,你的手机上一定少不了淘宝客户 ...

  8. 各个版本 Windows 10 系统中自带的 .NET Framework 版本

    原文各个版本 Windows 10 系统中自带的 .NET Framework 版本 Windows 名称 Windows 版本 自带的 .NET Framework 版本 Windows 10 Oc ...

  9. Blend_Effect

    原文:Blend_Effect 版权声明:本文为博主原创文章,未经博主允许不得转载. https://blog.csdn.net/u010265681/article/details/76651796 ...

  10. WPF 圆形Loading

    原文:WPF 圆形Loading 版权声明:本文为博主原创文章,未经博主允许不得转载. https://blog.csdn.net/a771948524/article/details/9271933 ...