前言:
在ES6以前,在应用程序中的每一个js代码中定义的一切都共享一个全局作用域。随着Web应用程序变得越来越复杂,js代码的使用量不断增大,这种做法会引起一些问题,如命名冲突和安全问题。ES6的目标之一是解决作用域的问题,为了使应用程序变得有序方便开发者,于是引进了模块。
什么是模块:
模块是自动运行在严格模式下并且不能自动退出运行的js代码。在模块顶部创建的变量不会自动被添加到全局共享作用域,这个变量仅在模块的顶级作用域中存在,模块导出一些外部代码可以访问的元素,如变量和函数。在模块的顶部,this的值是undefined,并且不支持html风格的代码注释。模块真正魔力所在是仅仅导出和导入你所需要的绑定。
导出的基本语法:
可以用export关键字将一部分已发布的代码暴露给其他模块。
//导出数据
export var color = "purple";
export let name = "hyc";
export const luckyNumber = 8;
//导出函数
export function add(num1, num2) {
return num1 + num2;
}
tips:除非使用关键字default否则导出的函数需要有一个名称。
导入的基本语法:
import { identifier1, identifier2 } from './example.js'
import * as example from './example.js'
import * as example from 'example'
tips:上面有三种导入方式,其中第一种代表从一个本地的js文件导入identifier1, identifier2的元素,第二种代表加载整个模块到example对象,可以通过访问example属性的方式调用,这种格式成为命名空间导入,第三种代表导入的是一个包。第一种导入方式看上去和解构对象很相似,但它不是,而是类似于使用const定义。不能再定义一个与之同名的变量,也不能修改绑定的值。值得注意的是不管在import语句中把一个模块写了多少次,该模块只执行一次,实例化过的模块被保持在内存中方便重复使用。
export和import的一个重要限制是:必须在其他语句和函数之外使用,不能有条件导出或以任何动态方式导出,这样做可以让js引擎静态地确定那些可以导出,因此只能在模块顶部使用。
导入绑定的一个微妙怪异之处:
标识符只有在被导出的模块中可以修改
export var number = 3;
export function changeNum(newNumber) {
number = newNumber
}
import { number } from './example.js';
import { changeNum } from './example'
changeNum(13);
console.log(number) //13
tips:实际上执行changeNum是到了原来的模块中去执行,所以所有引用该模块的代码都能获知变化。
导入默认值:
import sum from './example.js';
这条import语句从模块中导入了默认值,本地名称sum用于表示模块导出的任何默认函数。
export let color = "purple";
export default function sum(num1, num2) {
return num1 + num2;
}
import sum, { color } from './example.js'
tips:在import语句中。默认值必须在非默认值之前
虽然模块中的顶层变量、函数。。不会自动出现在全局作用域中,但这并不代表模块不能访问全局作用域。某些模块可能不导出任何东西,他们可能只修改全局作用域中的对象。无绑定导入最有可能被应用于创建Polyfill和Shim。
加载模块:
虽然es6定义了模块的语法,但它并没有定义如何加载这些模块。它将加载机制抽象到一个未定义内部方法HostResolveImportedModule中。Web浏览器和Node.js开发者可以通过对各自环境的认知来决定该如何实现。
在浏览器中使用模块:
1.在<script>元素中通过src属性指定加载,需指定type="mudule"
2.将js代码内嵌到内联脚本
<script type="module" src="http://www.example.com/mudule/example.js"
<script>
import example from './example.js'
<script>