1、什么是模块
模块试运行在严格模式下并且没有办法退出运行的JavaScript代码。
①、在模块顶部创建的变量不会自动被添加到全局共享作用域,模块必须导出一些外部代码可以访问的元素。模块也可以从其他模块导入绑定。
②、在模块的顶部,this的值是undefined
③、模块不支持HTML风格的代码注释
2、导出的基本语法
// 导出数据
export var color = "red";
export let name = "zhangdasan";
export const magicNum = 4;
// 导出函数
export function(num1,num2){
return num1+num2;
}
// 导出类
export class Rectangle{
constructor(length,width){
this.length = length;
this.width = width;
}
}
// 定义一个函数,之后将它导出
function multiply(num1,num2){
return num1*num2;
}
export multiply;
3、导入的基本语法
import { identifier1, identifier2 } from "./example.js";
必须把文件名后缀带上。
Node.js遵循基于文件系统前缀区分本地文件和包的惯例。即example是一个包而./example.js是一个本地文件。
从模块中导入一个值,就如同用const定义的一样。
1、导入单个模块
import { sum } from "./example.js";
console.log(sum(1,2)); // 3
sum = 1; // 报错
备注:为了更好的兼容浏览器和Node.js环境,一定要在字符串之前包含/、./或../来表示要导入的文件。
2、导入多个绑定
import { sum, multiply, magicNum } from "./example.js";
console.log(sum(1,magicNum)); // 5
console.log(multiply(1,magicNum)); // 4
3、导入整个模块
import * as example from "./example.js";
console.log(example.sum(1+example.magicNum)); // 5
console.log(example.multiply(1*example.magicNum)); // 4
这种导入格式被称为命名空间导入。
不管在import语句中把一个模块写了多少次,该模块都只执行一次。
限制:export和import必须在其他语句和函数之外使用。只能在模块顶部使用export和import,他们被设计成静态的关键字。
4、导入绑定的一个微妙怪异之处
ES6的import语句为变量、函数、类创建的是只读绑定,不可写。
export var name = "zhangdasan";
export function setName(newName){
name = newName;
}
import { name, setName } from "./example.js";
console.log(name); // "zhangdasan"
setName("lixiaosi");
console.log(name); // "lixiaosi"
name = "wangzhongwu"; // 抛出错误
4、导出和导入时重命名
可以使用as关键字在导出和导入的时候对模块重命名。
function sum(num1,num2){
return num1+num2;
}
export { sum as add };
import { add as sum } from "./example.js";
console.log(typeof add); // "undefined"
console.log(sum(1,2)); // 3
5、模块的默认值
模块的默认值指的是通过default关键字指定的单个变量、函数和类,只能为每个模块设置一个默认的导出值,导出时多次使用default关键字是一个语法错误。
1、导出默认值
export default function(num1,num2){
return num1+num2;
}
或者
function sum(num1,num2){
return num1+num2;
}
export default sum;
或者
function sum(num1,num2){
return num1+num2;
}
export { sum as default }
default是JavaScript中的默认关键字,不能用于变量、函数、类的名称。但是,可以将其用作属性名称。
2、导入默认值
import sum from "./example.js";
注意:sum表示example模块导出的任何默认函数。
例如:
export let color = "red";
export default function(num1,num2){
return num1+num2;
}
import sum, { color } from "./example.js";
console.log(sum(1,2)); // 3
console.log(color); // "red"
注意:本地默认值必须排在非默认值前面。
也可以在导入默认值时使用重命名语法。
import { default as sum, color } from "./example.js";
6、重新导出一个绑定
export { sum } from "./example.js";
或者
export |{ sum as add } from "./example.js";
或者
export * from "./example.js";
导出一切是指导出默认值和所有命名导出值。
7、无绑定导入
某些模块可能不导出任何东西,他们只是修改全局作用域中的对象。
例如:
Array.prototype.pushAll = function(item){
if(!Array.isArray(items)){
throw new TypeError("参数必须是一个数组");
}
return this.push(...items);
}
导入可以这样写:
import "./example.js";
let colors = ["red","yellow","blue"];
let items = [];
items.push(colors);
备注:无绑定导入最有可能被应用于创建Polyfill和Shim
8、加载模块
1、在Web浏览器中使用模块
ES6出现以前,Web浏览器加载JavaScript脚本的方法有:
①、在<script>元素中通过src属性制定一个加载代码的地址来加载JavaScript代码文件。
②、将JavaScript代码内嵌到没有src属性的<script>元素中。
③、通过Web Worker或Service Worker的方法加载并执行JavaScript代码文件。
A、在<script>中使用模块
<script>元素的默认行为是将JavaScript文件作为脚本加载,当type属性确实或包含一个JavaScript内容类型(如"text/javascript")时就会按脚本加载。
当type属性的值为"module"时,浏览器将所有内敛代码或包含在src指定的文件中的代码按照模块的方式加载。
<script type="module" src="module.js"></script>
或者
<script type="module">
import { sum } from "./example.js";
let result = sum(1,2);
</script>
备注:当浏览器无法识别type="module"时会忽略<script type="module">来提供良好的向后兼容性。
B、Web浏览器中的模块加载顺序
<script type="module">执行时会自动应用defer属性,来保证模块相关的其他模块被正确的加载。
模块按照它们出现在HTML文件中的顺序下载,所有模块下载完毕后,从头开始依次执行。下载解析过程和执行过程都是同步过程。
C、Web浏览器中的异步模块加载
<script>元素的async属性
只要当前模块相关的模块被加载解析完成便会执行,而不必等HTML文件中所有的模块是否下载解析完成。
D、将模块作为Worker加载
创建新Worker的步骤:创建一个新的Worker实例,传入JavaScript文件的地址。默认的加载机制是按照脚本的方式加载文件。
可以传入第二个参数来指定按模块的方式加载文件。
let worker = mew Worker("module.js",{ type: "module"});
所有浏览器中的Worker类都支持第二个参数。
2、浏览器模块说明符解析
浏览器要求模块说明符(import导入模块的路径)具有以下几种格式之一:
①、以/开头的解析为从根目录开始
②、以./开头的解析从当前目录开始
③、以../开头的解析conf父目录开始
④、URL格式(绝对路径)