ES6 —(Module 的语法)

1、概述

  在 ES6 之前,社区制定了一些模块加载方法,最主要的有 CommonJS 和 AMD 两种,前者用于服务器,后者用于浏览器。 ES6 在语言层面上,实现了模块功能,而且实现相当简单,完全可以取代 CommonJS 和 AMD 规范,称为浏览器和服务器通用的模块解决方案。
  ES6 模块的设计思想,是尽量的模块化,使得编译时就能确定模块的依赖关系,以及输入和输出的变量。CommonJS 和 AMD 模块,都只能在运行时确定这些东西。比如,CommonJS 模块是对象,输入时必须查找对象属性,并且加载时是先加载整个模块对象,然后获取对象上的方法,这种加载方式称为“运行时加载”,因为只有运行时才能得到这个对象,导致完全没有办法在编译时做“静态优化。
  ES6 模块不是对象,而是通过 export 命令显式指定输出的代码,再通过 import 命令输入。这导致了没法引用 ES6 模块本身,因为它不是对象。

ES6 模块的好处:

  • ES6 模块加载只加载需要的方法,其他方法不加载,这种加载称为编译时加载或静态加载,即 ES6 可以在编译时就完成模块加载,效率要比 CommonJS 模块加载的方式高。
  • 不再需要 UMD 模块格式,将来服务器和浏览器都会支持 ES6 模块格式。目前,通过各种工具库,其实已经做到了这一点。
  • 将来浏览器的新 API 就能用模块格式提供,不再必须做出全局变量或者 navigator 对象的属性。
  • 不再需要对象作为命名空间(比如 Math 对象),未来这些功能可以通过模块提供。

2、export 命令

  模块功能主要由两个命令构成:export 和 import 。export 命令用于规定模块的对外接口, import 命令用于输入其他模块提供的功能。
  一个模块就是一个独立的文件,该文件内部的所有变量(或函数、类),外部无法获取。如果你希望外部能够读取模块内部的某个变量(或函数、类),就必须使用 export 关键字输出该变量(或函数、类)。
  使用 export 输出变量、函数、类,有两方式:

  • 将 export 直接放在 varfunctionclass 语句前

    export var firstName = 'Michael'
    export function foo(){}
    
  • 将 export 放在最顶层环境中 使用大括号指定所有要输出的一组变量(或函数、类)。

    var firstName = 'michael';
    var lastName = 'Jackson';
    function foo(){}
    export {firstName, lastName, foo}
    

  在使用第二种方式的时候可以使用 as 关键字重命名。重命名后,变量(或函数、类)可以用不同的名字输出两次。

export {
    foo as foo1,
    foo as foo2
}

注意:
  (1)export 命令规定的是对外的接口,必须与模块内部的变量(或函数、类)建立一种一一对应关系。(使用第二种方式的时候必须使用大括号,否则报错)

// 以下全部会报错
export 1;  // 直接输出 1
export {1}; // 不符合命名规范

var m = 1;
export m; // 通过变量 m ,还是直接输出 1

function f(){}
export f;

  (2)export 语句输出的接口,与其对应的值是动态绑定的关系,即通过该接口,可以取到模块内部实时的值。

export var a = 'bar';
setTimeout(() => a = 'baz', 500);

上面代码输出变量 a ,值为 bar ,500 毫秒后变成 baz。
  (3)export 命令可以出现在模块的任何位置,只要处于模块顶层就可以。如果处于块级作用域内,就会报错。

function f(){
    export var a = 1; 
// 报错 Syntax error: 'import' and 'export' may only appear at the top level
} 

3、import 命令

  使用 export 命令定义了模块的对外接口以后,其他 JS 文件就可以通过 import 命令加载这个模块。
  import 命令接受一对大括号,里面指定要从其他模块导入的变量(或函数、类),大括号里面的接口名,必须与被导入模块对外接口的名称相同,即 import 的名称与 export 的名称要相同。在 import 命令中可以使用 as 关键字给接口重命名。
  import 后面的 from 指定模块文件的位置,可以是相对路径,也可以是绝对路径,并且 .js 后缀可以省略。如果只是模块名,不带有路径,那么必须有配置文件告诉 JS 引擎模块的位置。

import {firtsName, lastName as lN } from './test.js'

注意:
  (1)import 命令有提升效果,会提升到整个模块的头部,首先执行。

foo();
import {foo} from './test';

  (2)由于 import 是静态执行,所以不能使用表达式和变量,这些只有在运行时才能得到结果的语法结构。

// 以下语句会报错
import {'f' + 'oo'} from './test';

let module = './test';
import {foo} from module;

if( true ){
    import {foo} from './test';
}   

  (3)import 语句会执行所加载的模块,并且只执行一次。因此可以有下面的写法。

import 'load';
import 'load';

import {foo} from './test';
import {bar} from './test';
=> import {foo, bar} from './test';

上述代码加载了两次 load ,但是这会执行一次。
  (4)import 导入的变量(或函数、类)应该是可以静态分析的,所以不允许运行时改变。

import {foo} from './test';
foo = 'bar'; // 报错,Syntax error: "foo" is read-only

  (5)除了指定加载某个输出值,还可以使用整体加载,即使用星号(*)指定一个对象,所有输出值都加载在这个对象上面。

import  * as all  from './test';
使用时: all.foo

4、export default 命令

  使用 import 命令的时候,用户需要知道所要加载的变量名或函数名,否则无法加载。为了给用户提供方便,export default 命令为模块指定默认输出。

export default function(){}  // test.js 中指定默认输出一个函数

import foo from './test';  // 加载模块时,import 为该匿名函数指定任意名字
foo();

注意:
  (1)使用 export default 输出的默认变量(或函数、类),在加载时,import 后面不使用大括号。
  (2)export default 命令也可以导出非匿名函数。但是非匿名函数的名称只有在模块内部有效。在加载额时候,视同匿名函数加载。

export default function foo(){}
或
function foo(){}
export default foo;

  (3)使用 export default 时,对应的 import 语句不能使用大括号,否则会报错;使用 export 时,对应的 import 语句必须使用大括号,否则会报错。
  (4)export default 命令用于指定模块的默认输出,显然一个模块只能有一个默认输出,因此, export default 只能使用一次,否则会报错。
  (5)export default 本质上是输出一个叫做 default 的变量或者方法,然后在加载时指定任意名称。

function foo(){}
export {foo as default}; => export default foo;

import xxx from './t'; => import { default as xxx} from './t';

  (6)export default 命令实际输出的一个 default 的变量,所以他后面不能跟变量声明语句。

export var a = 1; // 正确

var a = 1;
export default a; // 正确

export default var a = 1; // 错误

  (7)export default 命令实际上是将后面的值赋给 default 并输出,所以可以直接将一个值写在export default 之后。

export default 4; // 正确
export 4; // 错误,因为没有指定对外的接口,而上一句指定了 default 为对外接口

  (8)可以在 export default 命令中同时输入默认方法和其他方法。

import _, { each, each as forEach } from 'lodash';

  (9)export default 可以用来输出类。

5、export 与 import 的复合写法

  如果在一个模块之中,先输入后输出同一个模块,import 语句可以与 export 语句写在一起。

export {foo. bar} from './test';
// 等同于
import {foo, bar} from './test';
export {foo, bar};

export {foo as myFoo} from './test'; // 接口改名
export * from './test'; // 整体输出(忽略 test 的 default 方法)
export {default} from 'foo'; // 默认接口
export {foo as default} from './test'; // 具名接口改为默认接口
export {default as foo} from 'foo'; // 默认接口改为具名接口

  以下三种 import 语句没有复合写法。

import * as someIdentifier from 'someModule';
import someIdentifier from 'someModule';
import someIdentifier, {nameIdentifier} from 'someModule';

6、模块的继承

  模块之间也可以继承。在子模块中将父模块先输入后输出,同时在扩展模块的方法即可。

// super.js
export function foo(){}

// sub.js
export * from './super';
export function bar(){}

上述代码中,sub 继承了 superfoo 方法,并定义了自己的 bar 方法,使用时加载 sub.js 就可得到 foobar 方法。

7、跨域常量

  const 声明的常量只在当前代码块有效。如果设置跨域模块的常量(即跨多个文件),或者说一个值被多个模块共享,可以采用 export 将其输出出去。

阮一峰:ECMAScript 6入门

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值