ES6 module
Posted Jmytea
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了ES6 module相关的知识,希望对你有一定的参考价值。
概述
历史上,javascript 一直没有模块(module)体系,无法将一个大程序拆分成互相依赖的小文件,再用简单的方法拼装起来。其他语言都有这项功能,比如 Ruby 的require、Python 的import,甚至就连 CSS 都有@import,但是 JavaScript 任何这方面的支持都没有,这对开发大型的、复杂的项目形成了巨大障碍。
在 ES6 之前,社区制定了一些模块加载方案,最主要的有 CommonJS 和 AMD 两种。前者用于服务器,后者用于浏览器。ES6 在语言标准的层面上,实现了模块功能,而且实现得相当简单,完全可以取代 CommonJS 和 AMD 规范,成为浏览器和服务器通用的模块解决方案。
设置浏览器启用es6语法功能:(Chrome 61+,Firefox 54+)
1.在浏览器的url中输入:chrome://flags/
2.搜索 JavaScript 关键字
3.设置选项 Experimental JavaScript 为 Enable
4.重启浏览器后生效
script 标签里面要加 type="module", 这样浏览器才会把相关的代码当作ES6的module 来对待
不能写“裸”路径,即使是同一层级下面的文件,也要加上 './name.js'
ES6 的模块自动采用严格模式,不管你有没有在模块头部加上"use strict"。
ES6 模块功能主要由两个命令构成:export 和 import。
export命令用于规定模块的对外接口,import命令用于输入其他模块提供的功能。
一个模块就是一个独立的文件,该文件内部的所有变量,外部无法获取。
export 关键字
如果你希望外部能够读取模块内部的某个变量,就必须使用export关键字输出该变量。
以下代码放在 module1.js 文件中
// 不推荐
export var userName = 'xiaoming';
export var userSex = '男';
export var userAge = 27;
// 推荐
var userName = 'xiaoming';
var userSex = '男';
var userAge = 27;
export userName, userSex, userAge ;
export命令除了输出变量,还可以输出对象、函数或类(class)。
export function multiply(x, y)
return x * y;
;
上面代码对外输出一个函数multiply。
通常情况下,export输出的变量就是本来的名字,但是可以使用as关键字重命名。
function f1() ...
function f2() ...
export
f1 as test1,day23-2
f2 as test2,
f2 as test3
;
上面代码使用as关键字,重命名了函数f1和f2的对外接口。重命名后,f2可以用不同的名字输出两次。
错误的写法:
// 报错
export 1;
// 报错
var m = 1;
export m;
// 报错
function f()
export f;
正确的写法:
// 正确
export var m = 1;
// 正确
var m = 1;
export m;
// 正确
var n = 1;
export n as m;
// 正确
export function f() ;
// 正确
function f()
export f;
export语句输出的接口,与其对应的值是动态绑定关系,即通过该接口,可以取到模块内部实时的值。
export var abc = '123';
setTimeout(() => abc = '456', 500);
上面代码输出变量abc,值为'123',500 毫秒之后变成'456'。
import 关键字
使用export命令定义了模块的对外接口以后,其他 JS 文件就可以通过import命令加载这个模块。
以下代码放在 test1.js 文件中
import userName, userSex, userAge from './module1.js';
console.log(userName);
console.log(userSex);
console.log(userAge);
如果想为输入的变量重新取一个名字,import命令要使用as关键字,将输入的变量重命名。
import userName as xm from './module1.js';
import命令输入的变量都是只读的,因为它的本质是输入接口。也就是说,不允许在加载模块的脚本里面,改写接口。
import a from './xxx.js'
a = 123; // Syntax Error : 'a' is read-only;
如果a是一个对象,改写a的属性是允许的。
import a from './xxx.js'
a.foo = 'hello'; // 合法操作
注意,import命令具有提升效果,会提升到整个模块的头部,首先执行。
show();
import show from './module1.js';
除了指定加载某些输出值,还可以使用整体加载,即用星号(*)指定一个对象,所有输出值都加载在这个对象上面。
import * as obj from './module1.js';
console.log(obj.userName);
console.log(obj.userSex);
console.log(obj.userAge);
export default 命令
使用import命令的时候,用户需要知道所要加载的变量名或函数名,否则无法加载。
为了给用户提供方便,用export default命令,为模块指定默认输出。
下面比较一下默认输出和正常输出:
// 第一组
export default function test() // 输出
// ...
import test from './test.js'; // 输入
// 第二组
export function test() // 输出
// ...
;
import test from './test.js'; // 输入
第一组是使用export default时,对应的import语句不需要使用大括号;
第二组是不使用export default时,对应的import语句需要使用大括号。
一个模块只能有一个默认输出,因此export default命令只能使用一次。
因为export default命令其实只是输出一个叫做default的变量,所以它后面不能跟变量声明语句。
// 正确
export var a = 1;
// 正确
var a = 1;
export default a;
// 错误
export default var a = 1;
// 正确
export default 42;
// 报错
export 42;
跨模块常量
如果想设置跨模块的常量(即跨多个文件),或者说一个值要被多个模块共享,可以采用下面的写法。
// constants.js 模块
export const A = 1;
export const B = 3;
export const C = 4;
// test1.js 模块
import * as constants from './constants';
console.log(constants.A); // 1
console.log(constants.B); // 3
// test2.js 模块
import A, B from './constants';
console.log(A); // 1
console.log(B); // 3
浏览器加载规则
默认情况下,浏览器是同步加载 JavaScript 脚本,即渲染引擎遇到<script>标签就会停下来,等到执行完脚本,再继续向下渲染。如果是外部脚本,还必须加入脚本下载的时间。
如果脚本体积很大,下载和执行的时间就会很长,因此造成浏览器堵塞,用户会感觉到浏览器“卡死”了,没有任何响应。
浏览器允许脚本异步加载,下面就是两种异步加载的语法:
<script src="path/to/myModule.js" defer></script>
<script src="path/to/myModule.js" async></script>
上面代码中,<script>标签打开defer或async属性,脚本就会异步加载。渲染引擎遇到这一行命令,就会开始下载外部脚本,但不会等它下载和执行,而是直接执行后面的命令。
defer与async的区别是:
defer要等到整个页面在内存中正常渲染结束(DOM 结构完全生成,以及其他脚本执行完成),才会执行;
async一旦下载完,渲染引擎就会中断渲染,执行这个脚本以后,再继续渲染。
一句话,defer是“渲染完再执行”,async是“下载完就执行”。
如果有多个defer脚本,会按照它们在页面出现的顺序加载,而多个async脚本是不能保证加载顺序的。
浏览器加载 ES6 模块,也使用<script>标签,但是要加入type="module"属性。
<script type="module" src="./foo.js"></script>
上面代码在网页中插入一个模块foo.js,由于type属性设为module,所以浏览器知道这是一个 ES6 模块。
浏览器对于带有type="module"的<script>,都是异步加载,不会造成堵塞浏览器,即等到整个页面渲染完,再执行模块脚本,等同于打开了<script>标签的defer属性。
<script type="module" src="./foo.js"></script>
<!-- 等同于 -->
<script type="module" src="./foo.js" defer></script>
如果网页有多个<script type="module">,它们会按照在页面出现的顺序依次执行。
ES6 模块也允许内嵌在网页中,语法行为与加载外部脚本完全一致。
<script type="module">
import utils from "./utils.js";
// other code
// ...
</script>
对于外部的模块脚本(如上例的foo.js),有几点需要注意:
代码是在模块作用域之中运行,而不是在全局作用域运行。
模块内部的顶层变量,外部不可见。
模块脚本自动采用严格模式,不管有没有声明use strict。
模块之中,可以使用import命令加载其他模块,也可以使用export命令输出对外接口。
模块之中,顶层的this关键字返回undefined,而不是指向window。
同一个模块如果加载多次,将只执行一次。
以上是关于ES6 module的主要内容,如果未能解决你的问题,请参考以下文章