Module 模块加载

ES6 模块规范

ES6 的模块功能主要由两个命令构成: exportimport
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

你可能感兴趣的:(Module 模块加载)