Chinaunix首页 | 论坛 | 博客
  • 博客访问: 817638
  • 博文数量: 62
  • 博客积分: 526
  • 博客等级: 二等列兵
  • 技术积分: 2078
  • 用 户 组: 普通用户
  • 注册时间: 2012-02-04 20:41
个人简介

博客迁移至 freefe.cc

文章分类

全部博文(62)

分类: JavaScript

2014-10-09 18:14:37

    

    原文地址:
    原文作者:Dr. Axel Rauschmayer

    译者:倪颖峰
    地址:lovenyf.blog.chinaunix.net


    在2014年7月底,TC39的另外一个会议,敲定了关于 ECMAScript 6 模块化语法的最后一些细节。本文完整的概述了关于 ES6 模块系统。

1. 当前 JavaScript 的模块系统。


    JavaScript 目前没有内置方法来支持模块化,不过社区创造了非常不错的变通方案。两个重要的(可惜相互不兼容)标准是:

    1. CommonJS 模块化:该标准主要被使用在 Node.js 中(Node.js 模块化中有一些超出 CommonJS 的语法的特性)。
        特点:语法简洁,为同步加载设计,主要使用在服务器端。
    2. 异步模块定义(AMD):该标准最流行的应用就是 RequireJS。
        特点:略微复杂的语法使得 AMD 可以不使用 eval() 实现(或者说编译过程),为异步加载设计,主要在浏览器端使用。

    以上只是对目前的状况简单粗暴的解释了一下,如果你想更深入的了解一下可以读一下 Addy Osmani 的“使用AMD,CommonJS 和 ES Harmony 写模块化JavaScript”。


2. ECMAScript 6 模块化


    ECMAScript 6 模块化的目标是创建一种 CommonJS 和 AMD 使用者都乐意接受的方式:
    1. 类似 CommonJS,拥有简洁的语法,倾向于单一的接口并且支持循环依赖。
    2. 类似 AMD,直接支持异步加载和配置模块加载。

    内置语言允许 ES6 模块化超出 CommonJS 和 AMD 规范(之后会详细介绍):
    1. 语法将比 CommonJS 的更简洁。
    2. 结构可以做静态分析(静态检测,优化等)。
    3. 比 CommonJS 做的更好的循环依赖。

    ES6 模块化标准包括两部分:
    1. 声明语法(引入与导出)。
    2. 编程式加载接口:用来配置如何加载模块和按条件加载模块。

3. ES6 模块语法概述


    有两种导出方式:命名式导出(每个模块可以多个)和定义式导出(每个模块仅一个)。

3.1. 命名式导出(每个模块可以多个)

    
    一个模块可以通过前缀关键词 export 声明来导出多个。
    这些导出以名字进行区分,称之为命名式导出。

  1. // ----- lib.js ------
  2. export const sqrt = Math.sqrt;
  3. export function square( x ){
  4.     return x*x;
  5. }
  6. export function diag( x, y ){
  7.     return sqrt( square( x ) + square( y ) );
  8. }

  9. // -------- main.js -------
  10. import{ square, diag } from 'lib';
  11. console.log( square(11) ); // 121
  12. console.log( diag( 4, 3 ) ); // 5

    也有其他方式带定义命名式导出(稍后介绍),但是我发现此方式是最方便的:如果没有外层环境,你可以很简单的书写代码,然后以你想要的关键词来标识所有的东西。

    如果需要,你也可以导入整一个模块,通过属性命名来指向命名式的导出:

  1. import * as lib grom 'lib';
  2. console.log( lib.square( 11 ) ); // 121
  3. console.log( lib.diag( 4, 3 ) ); // 5


    在 CommonJS 语法下的相同代码:有段时间我在 Node.js 下尝试了几种不错的策略来减少我的模块导出时代码冗余。现在我比较喜欢下面这种简单但是略微冗长的风格,有点类似于模块模式:

  1. // ---- lib.js -----
  2. var sqrt = Math.sqrt;
  3. function square( x ){
  4.     return x*x;
  5. }
  6. function diag( x, y ){
  7.     return sqrt( square( x ) + square( y ) );
  8. }

  9. module.exports = {
  10.     sqrt : sqrt,
  11.     sqare : sqare,
  12.     diag : diag
  13. }

  14. // ------ main.js ------
  15. var square = require( 'lib' ).square;
  16. var diag = require( 'lib' ).diag;
  17. console.log( square( 11 ) ); // 121
  18. console.log( diag( 4, 3 ) ); // 5


3.2. 定义式导出(每个模块仅一个)


    在 Node.js 社区很受欢迎的模块导出单一值。当然在需要经常有构造函数和类来创建模型的前端开发中也类一样,一模块一模型。一个 ECMAScript 6 模块可以采用定义式导出,导出最主要的值。定义式导出容易引用。

    以下为单函数的 ECMAScript 6 模块:

  1. // ----- myFunc.js -------
  2. export default function(){ };

  3. // ------ main.js --------
  4. import myFunc from 'myFunc';
  5. myFunc();

    定义式导出一个类的 ECMAScript 6 模块如下:

  1. // ----- MyClass.js -------
  2. export default class(){ };

  3. // ------ main.js --------
  4. import MyClass from 'MyClass';
  5. let inst = new MyClass();

    注:定义式导出声明的操作数是一个表达式,往往不需要名字。代替之的是通过模块的名称来确认。

3.3. 同一模块中使用命名式导出和定义式导出


    一下模式在 JavaScript 中非常常见:一个库仅仅是单一的函数,不过通过该函数的属性来提供其他的支持。包括 jQuery 和Underscore.js。下面是简单的 Underscore 作为 CommonJS 模块的写法:

  1. // ----------underscore.js ----------
  2. var _ = function( obj ){};
  3. var each = _.each = _forEach = function( obj, iterator, context ){}

  4. module.esports = _;

  5. // ---------- main.js ---------
  6. var _ = require('underscore');
  7. var each = _.each;

    在ES6下,函数 _ 是定义式导出而 each 和 forEach 是命名式导出。 这证明事实上是可以同时进行命名式导出和定义式导出的。例如之前的 CommonJS 模块,以 ES6 来重写模块,如下:

  1. // --------underscore.js--------------
  2. export default function( obj ){ }
  3. export function each( obj, itertor, context ){ }
  4. export { each as forEach }

  5. // --------main.js ------------
  6. import _, { each } from 'underscore';

    需要注意的是 CommonJS 版本和 ECMAScript 6 版本仅仅是大致相同。后者为扁平式结构,而前者为嵌套式结构。喜欢哪一种风格有自己决定,不过扁平式结构具有做静态分析的优势(下面会提到优点)。CommonJS 风格看起来为了满足对象的需要部分被作为命名空间,可以通过 ES6 模块来实现这种需求并且导出实现。

    定义式导出仅仅是另一种形式的命名式导出。

    定义式导出事实上是命名式导出使用了特殊名称 default 。即,下面两个是等价的:

  1. import { default as foo } from 'lib';
  2. import foo from 'lib';

    类似的,下面两者也是等价的定义式导出:

  1. // --------module1.js-----------
  2. export default 123;
  3. // --------module2.js-----------
  4. const D = 123;
  5. export { D as default };

    那为何我们还需要命名式到导出呢?
    你可能会疑虑,为何我们还需要命名式导出,而不是类似于 CommonJS 形式的单一定义式导出对象?答案就是,你不能通过对象来强制执行一个静态结构并且会丢失相关的优势(下一节描述)。


    译者:倪颖峰
    地址:lovenyf.blog.chinaunix.net
    (转载请注明出处)





阅读(7482) | 评论(0) | 转发(1) |
给主人留下些什么吧!~~