ES6 模块规范
ES6 的模块功能主要由两个命令构成: export
和 import
。
export 命令用于规定模块的对外接口。
import 命令用于输入其他模块提供的功能。
ES6 的模块自动采用严格模式,不管你有没有在模块头部加上"use strict";。
export 命令
export 的写法如下:
// 写法一
export var foo = 1;
// 写法二
var foo = 1;
export {foo };
// 写法三
var foo = 1;
export {n as foo };
foo 可以是变量,常量,函数function
或 类class
import 命令
// 写法一
import { foo } from 'util'
import { bar} from 'util'
// 写法二
import { foo, bar } from 'util'
// 写法三
import { foo as surname } from 'util';
// 写法四
import * as u from 'util';
console.log(u.foo, u.bar)
01
import 后面的 from 指定模块文件的位置,可以是相对或绝对路径,.js 后缀可以省略。
如果只是模块名,不带路径,那么必须有配置文件来告诉JavaScript引擎该模块的位置。
例如上面的 util 是模块文件名,由于不带有路径,必须是配置过的,告诉引擎怎么获取这个模块。
02
import命令输入的变量都是只读的,不允许在加载模块的脚本里面改写接口。
import {a} from './xxx.js'
a = {}; // Syntax Error : 'a' is read-only;
a.foo = 'hello'; // 合法操作
上面代码中,脚本加载了变量a,对其重新赋值就会报错,因为a是一个只读的接口。但是,如果a是一个对象,改写a的属性是允许的。
第三行中a的属性被成功改写,并且其他模块也可以读到改写后的值。不过,这种写法很难查错,不建议这么做。
03
import 语句是 Singleton 模式的。既同一个模块实例被 import 两次时,只会执行一次。
04
目前阶段,通过 Babel 转码,CommonJS 模块的require命令和 ES6 模块的import命令,可以写在同一个模块里面,但不建议这样做
export default 命令
export default 的写法如下:
// 写法一
export default function () { console.log('foo') }
// 写法二
export default function foo() { console.log('foo') }
// 写法三
function foo() { console.log('foo') }
export default foo
export default 命令可以使用匿名函数或非匿名函数。
但是非匿名函数的的函数名在模块外是无效的。在模块外加载时视同非匿名函数来加载。
01 export 和 export default 在输入上的不同
// 第一组
export function crc32() { // 输出
// ...
};
import { crc32 } from 'crc32'; // 输入 当不用 as 时import 必须与 export 的名字相同
// 第二组
export default function crc32() { // 输出
// ...
}
import c from 'crc32'; // 输入 可为该匿名函数指定任意名字
对比上面两种写法可知,使用 export default 时, 对应的 import 语句不需使用大括号。
这样的好处是当开发一些第三库时,用户未必愿意去了解模块有哪些属性和方法,这时为了用户方便,就可以用 export default 命令,为模块指定默认输出啦。
02 export 和 export default在定义时的不同
export default就是输出一个叫做
default`的变量或方法
// 正确
export var a = 1;
// 正确
var a = 1;
export default a;
// 错误
export default var a = 1;
上面代码中,export default a的含义是将变量a的值赋给变量default。所以,最后一种写法会报错。
同样地,因为export default命令的本质是将后面的值,赋给default变量,所以可以直接将一个值写在export default之后。
// 正确
export default 42;
// 报错
export 42;
CommonJS 模块规范
在 ES6 之前,社区指定了一些模块加载方案,主要的有 CommonJS 和 AMD 两种,前者用户服务器,后者用于浏览器。比如 CommonJS 模块是对象输出,输入时不需查找对象属性。
ES6 模块的设计思想是尽量的静态化,使得编译时就能确定模块的依赖关系,以及输入和输出的变量。CommonJS 和 AMD 模块,都只能在运行时确定这些东西。
module.exports 与 require
我们所熟悉的 NodeJS 由模块组成,采用的就是CommonJS 模块规范。
CommonJS定义的模块分为: 模块标识(module)、模块定义(exports) 、模块引用(require)
CommonsJS 规范规定,每个模块内部, module 变量代表当前模块。这个模块是一个对象,她的 exports 属性,来表示对外的接口(既 module.exports 是对外的接口)。
例如
var num = 5;
var add = function (value) {
return value + x;
};
module.exports.num = num;
module.exports.add = add;
上面代码的输入格式如下:
let { num, add } = require('util');
// 或者
let _util = require('util');
console.log( _util.num ) //5
console.log( _util.add(1) ) //6
module.exports 与 exports
为了方便,Node 为每一个模块提供一个 exports 变量,指向 module.exports。
他们的关系是:
exports = module.exports = {};
- exports 是 module.exports 的一个引用,指向同一个内存地址;
- module.exports 初始值为一个空对象{}, 所以 exports 初始值也是 {};
- exports.xxx = 相当于在导出对象上挂属性,该属性对调用模块直接可见;
- exports = 相当于给 exports 对象重新赋值,会切断了 exports 与 module.exports 的联系,所以建议使用 module.exports;
如果对上面说的一堆不理解,来看下面的例子:
// requireMe.js
// exports = function(){} // 错误写法 ,这样相当于直接给 exports 赋值
exports.something = function(){ console.log('hello world') }
// index.js
var something = require('./requireMe');
something();
// 报错,因为 require 出来的 module.exports 是一个 object,不能直接执行
//修改方式一
// requireMe.js
module.exports = function(){ console.log('hello world') }
// 不报错,因为此时的 module.exports 是一个 function, 可以直接执行。
// 修改方式二
// index.js
var something = require('./requireMe');
something.something();
// 因为这时候 require 出来的是一个 object,有一个 something 的属性,所以可以这样调用执行。
参考文章:
阮一峰:Module 语法
liaofy:module.exports与exports,export与export default之间的关系和区别
希望这篇文章能帮助到你
END