JavaScript起初没有官方的模块语法
在社区中诞生了野生规范:
- AMD —— 最古老的模块系统之一,最初由 require.js 库实现。
- CommonJS —— 为 Node.js 服务器创建的模块系统。
- UMD —— 另外一个模块系统,建议作为通用的模块系统,它与 AMD 和 CommonJS 都兼容。
ComonJs
CommonJS的核心思想就是通过 require 方法来同步加载所要依赖的其他模块,然后通过 exports 或者 module.exports 来导出需要暴露的接口。如下所示:
// a.js
let x = 5;
let addX = function (value) {
return value + x;
};
module.exports.x = x;
module.exports.addX = addX;
//b.js
let a = require('./a.js');
console.log(a.x); //5
console.log(a.addX(3)); //8
exports是对module.exports的引用,用法一样,如果有重名时以module.exports为准
- 通过require引入基础数据类型时,属于复制该变量
- 通过require引入复杂数据类型时,浅拷贝该对象(指向同一个对象)
- 出现模块之间的循环引用时,会输出已经执行的模块,而未执行的模块不输出
3可以看:https://www.cnblogs.com/unclekeith/p/7679503.html
ES6模块
ES6引入了模块功能
- export 关键字标记了可以从当前模块外部访问的变量和函数
- import 关键字允许从其他模块导入功能
由于模块支持特殊的关键字和功能,因此我们必须通过使用 <script type="module">
特性(attribute)来告诉浏览器,此脚本应该被当作模块(module)来对待。
<!doctype html>
<script src="./test.js" type="module"></script> // 加了type="module" 就不是全局变量了
//test.js
import {sayHi} from './say.js';
document.body.innerHTML = sayHi('John');
//say.js
export function sayHi(name){
return "hello "+name;
}
功能
模块和常规脚本相比的不同
始终使用"use strict"
模块默认使用严格模式,未声明的变量赋值会报错
<script type="module">
a = 5; // error
</script>
模块级作用域
常规的<script>
引用所有代码都在同一个全局作用域中
而每个模块有自己的作用域,一个模块中的变量和函数在其他脚本是不可见的
//index.html
<!doctype html>
<script type="module" src="a.js"></script>
<script type="module" src="b.js"></script>
//a.js
let a = 1;
//b.js
alert(a); // 报错
浏览器中,每个 <script type="module">
也存在独立的顶级作用域
<script type="module">
// 变量仅在这个 module script 内可见
let user = "John";
</script>
<script type="module">
alert(user); // Error: user is not defined
</script>
正确应该用export导出想被外部访问的内容,并import所需要的内容,后面导入导出再细说
模块代码仅在第一次导入时被解析
模块只执行一次,如果同一个模块被导入到多个其他位置,那它的代码仅会在第一次导入时执行,然后将导出的内容提供给其他所有导入
// admin.js
export let admin = {
name: "小红"
}
// a.js
import {admin} from './admin.js'
admin.name = '小李'
// b.js
import {admin} from './admin.js'
console.log(admin); // 小李
所以模块只执行了一次,生成导出并分享给了其他导入,某个地方修改后其他模块也能看到
我们可以在首次导入时初始化设置模块,只需要设置一次后其他导入就都可以直接使用了
在一个模块中,"this"是undefined
<script>
console.log(this); // window
</script>
<script type="module">
conso.log(this); // undefined
</script>
模块脚本是延迟的
等同于使用了defer
,即
- 下载外部模块脚本
eg.
<script type="module">
alert(typeof button); // object:脚本可以“看见”下面的 button
// 因为模块是被延迟的(deferred,所以模块脚本会在整个页面加载完成后才运行
</script>
<!-- 相较于下面这个常规脚本: -->
<script>
alert(typeof button); // button 为 undefined,脚本看不到下面的元素
// 常规脚本会立即运行,常规脚本的运行是在在处理页面的其余部分之前进行的
</script>
<button id="button">Button</button>
第二个脚本要优先于第一个脚本执行,模块脚本是被延迟的要等到HTML文档被处理完才会执行,而常规脚本则会立即运行