阅读更多

2顶
0踩

Web前端

原创新闻 全面解析ECMAScript 6模块系统

2015-05-04 09:48 by 副主编 mengyidan1988 评论(1) 有5746人浏览
【编者按】在任何一个大型应用中模块化是很常见的。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,所有的对象将被一起导出。请看下面的例子:
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)
2
0
评论 共 1 条 请登录后发表评论
1 楼 mangguo 2015-05-05 10:02
io.js中支持ES6,一直想学习。到现在还没有去行动。一直在学nodejs的课程 。等我把nodejs学的差不多了,就要动手ES6了。文章也不错,支持一下。

发表评论

您还没有登录,请您登录后再发表评论

相关推荐

  • 全面解析ECMAScript6模块系统

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

  • linux C编程(五)函数指针与函数调用

    函数指针函数指针,首先是一个指针,只不过该指针指向的是函数,而不是变量。#include <stdio.h>int max(int x,int y) { return (x>y)?x:y; } int main() { int i = 0; int (*f)(int x,int y) = max;//定义函数指针并初始化 //f = max;f = &max;也可使用这两种方式进行初

  • 函数指针,堆栈

     函数指针的特点 定义指向函数的指针变量的一般形式为数据类型(*指针变量名)(函数参数列表); 如int (*p)(int,int); 注意:对于函数指针来说p+n,p++,p—等运算无意义。 案例如下: #include #include   int add(int a, int b){ return a + b

  • ecma6 ajax 解析php,全面解析ECMAScript 6模块系统

    原标题:全面解析ECMAScript 6模块系统【编者按】在任何一个大型应用中模块化是很常见的。ES6的模块为Java提供了这个特性,并且为这些模块提供了许多选择方法来导出和引入对象。Ravi Kiran 在《Modules in ECMA 6 ...

  • 【ES6专栏】全面解析ECMAScript 6模块系统

    http://www.csdn.net/article/2015-04-30/2824595-Modules-in-ES6 转载于:https://my.oschina.net/li...

  • ECMAScript6中变量解构赋值的最全解析

    ES6允许按照一定模式,从数组和对象中提取值,对... 输入模块的指定方法 //加载模块时,往往需要指定输入哪些方法。解构赋值使得输入语句非常清晰。 const { SourceMapConsumer, SourceNode } = require("source-map");

  • ECMAScript6面试题

    ECMAScript6面试题

  • ECMAScript6 快速上手(新手必看)

    二、babel 一个es6的解析器 在我们正式讲解ES6语法之前,我们得先了解下Babel。 Babel是一个广泛使用的ES6转码器,可以将ES6代码转为ES5代码,从而在现有环境执行。大家可以选择自己习惯的工具来使用使用Babel,我最...

  • ECMAScript6

    ECMAScript和JavaScript的关系是,前者是后者的规格,后者是前者的一种实现 在webstrom中使用时需要配置 流程图 babel是什么 是一个工具链,主要用于在旧的浏览器或环境中奖ECMAScript2015+代码转换为向后...

  • 论ES6模块系统的静态解析

    本文是Dave Herman的《Static module resolution》一文的编译。Dave Herman是TC39的成员,ES6 module系统的champion。【ES6 spec太大了,所以分成许多可相对独立的特性集合,...在纯JS环境下已经有多种模块系统。比如

  • ECMAScript 6(总结)

    ECMAScript 6 简介 ECMAScript 6.0(以下简称 ES6)是 JavaScript 语言的下一代标准,已经在 2015 年 6 月正式发布了。 它的目标,是使得 JavaScript 语言可以用来编写复杂的大型应用程序,成为企业级开发语言。 ...

  • 掌握未来的 JavaScript:深入解析 ECMAScript 6 完全指南

    ECMAScript 6 不仅仅是对 JavaScript 的一次更新,它代表了一种向现代化、高效率和强大功能迈进的转变。通过深入学习和掌握 ES6,您将能够充分利用 JavaScript 的强大能力,编写更加清晰、高效和维护性更强的代码。...

  • JavaScript模块化解析

    文章目录JavaScript模块化全面解析一、什么是JavaScript的模块化?二、立即执行函数二、NodeJS给模块化带来的变化总结 JavaScript模块化全面解析 定义:模块化是指解决一个复杂问题时自顶向下逐层把系统划分成若干...

  • webstrom html校验css语法,webstorm ECMAScript 6 语法检查

    打开 webstorm 进行设置 ( File &gt;... ECMAScript 6 )。当你设置好 es6 规范之后, 使用webstorm编程, 右边的语法检错几乎没有红色和黄色的报错的时候, 恭喜你, 你已经养成了使用 es6 标准编程的习惯...

  • ECMAScript 6 (ES6) 变量的解构赋值

    我又回来了 继续学习ES6 争取早日变成技术大佬在小白面前指点江山装逼 变量的解构赋值 (1)基本用法 ES6 允许按照一定模式,从数组和对象中提取值,对变量进行赋值,这被称为解构(Destructuring)。...

  • ECMAScript

    文章目录 初识JavaScript 4.js特点: 6.全局作用域和局部作用域 : 7.在body和head中使用JavaScript的区别 ​ : 8.关键字与保留字 ECMAScript(js核心语法) 1.变量 2.数据类型 2.1基本/简单数据类型:Number、String...

  • 基于springboot教育资源共享平台源码数据库文档.zip

    基于springboot教育资源共享平台源码数据库文档.zip

  • 视频笔记linux开发篇

    linux开发篇,配套视频:https://www.bilibili.com/list/474327672?sid=4493702&spm_id_from=333.999.0.0&desc=1

Global site tag (gtag.js) - Google Analytics