【ES6】Moudle

概述

ES6之前,JS没有模块系统,无法将一个大程序拆分为互相依赖的小文件

社区制定了一些模块加载方案最主要的是CommonJS和AMD,前者用于服务器,后者用于浏览器。

// CommonJS模块,运行时加载
let { stat, exists, readFile } = require('fs');

ES6模块的设计思想是尽量静态化,使得编译时就能确定模块的依赖关系,以及输入和输出的变量。而上述两者方案只有在运行时才能确定。

// ES6模块,编译时已确定,效率更好
import { stat, exists, readFile } from 'fs';

当然,这也导致了没法引用 ES6 模块本身,因为它不是对象 

ES6模块采用严格模式

严格模式主要有以下限制。

  • 变量必须声明后再使用
  • 函数的参数不能有同名属性,否则报错
  • 不能使用with语句
  • 不能对只读属性赋值,否则报错
  • 不能使用前缀 0 表示八进制数,否则报错
  • 不能删除不可删除的属性,否则报错
  • 不能删除变量delete prop,会报错,只能删除属性delete global[prop]
  • eval不会在它的外层作用域引入变量
  • evalarguments不能被重新赋值
  • arguments不会自动反映函数参数的变化
  • 不能使用arguments.callee
  • 不能使用arguments.caller
  • 禁止this指向全局对象,ES6模块中this指向undefined
  • 不能使用fn.callerfn.arguments获取函数调用的堆栈
  • 增加了保留字(比如protectedstaticinterface

export命令

export命令用于规定模块的对外接口

// profile.js
// 输出变量、函数或类 export let a = 'lpr' export let year = 2020
export function multiply(x, y) { return x * y; }

或者

// profile.js
let a = 111
function v1() { ... }

export { firstName, v1 }

通常情况下,export输出的变量就是本来的名字,但是可以使用as关键字重命名

function v1() { ... }
function v2() { ... }

export {
  v1 as streamV1,
  v2 as streamV2,
  v2 as streamLatestVersion // 可以用不同的名字输出
}

export命令规定的是对外的接口,必须与模块内部的变量建立一一对应关系

export语句输出的接口,与其对应的值是动态绑定关系,即通过该接口,可以取到模块内部实时的值。

commonJS模块输出的是值得缓存,不存在动态更新

export命令可以出现在模块的任何位置,只要处于模块顶层就可以 

 

import命令

import命令用于输入其他模块提供的功能

import { a,b,c } from './profile.js';

如果想为输入的变量重新取一个名字,import命令要使用as关键字,将输入的变量重命名。 

import { lastName as surname } from './profile.js';

import命令输入的变量都是只读的,因为它的本质是输入接口。也就是说,不允许在加载模块的脚本里面改写接口

import后面的from指定模块文件的位置,可以是相对路径,也可以是绝对路径,.js后缀可以省略。如果只是模块名,不带有路径,那么必须有配置文件,告诉 JavaScript 引擎该模块的位置。

import命令具有提升效果,会提升到整个模块的头部,首先执行

import是静态执行,所以不能使用表达式和变量

模块的整体加载

import { area, circumference } from './circle';
// 整体加载
import * as circle from './circle';

export default 命令

使用import命令的时候,用户需要知道所要加载的变量名或函数名,否则无法加载。但是,用户肯定希望快速上手,未必愿意阅读文档,去了解模块有哪些属性和方法。

为了给用户提供方便,让他们不用阅读文档就能加载模块,就要用到export default命令,为模块指定默认输出。

// export-default.js 默认输出一个匿名函数
export default function () {
  console.log('foo');
}

其他模块加载该模块时,import命令可以为该匿名函数指定任意名字。 

// import-default.js
import customName from './export-default';
customName(); // 'foo'

比较一下默认输出和正常输出。 

// 第一组
export default function crc32() { // 输出
  // ...
}

import crc32 from 'crc32'; // 输入

// 第二组
export function crc32() { // 输出
  // ...
};

import {crc32} from 'crc32'; // 输入

export 与 import 的复合写法

export { foo, bar } from 'my_module';

// 可以简单理解为
import { foo, bar } from 'my_module';
export { foo, bar };

但需要注意的是,写成一行以后,foobar实际上并没有被导入当前模块,只是相当于对外转发了这两个接口,导致当前模块不能直接使用foobar。  

模块可以继承

跨模块常量

如果想设置跨模块的常量(即跨多个文件),或者说一个值要被多个模块共享,可以采用下面的写法。

 

// 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

浏览器加载

 传统方法


deferasync的区别是:defer要等到整个页面在内存中正常渲染结束(DOM 结构完全生成,以及其他脚本执行完成),才会执行;async一旦下载完,渲染引擎就会中断渲染,执行这个脚本以后,再继续渲染。一句话,defer是“渲染完再执行”,async是“下载完就执行”。另外,如果有多个defer脚本,会按照它们在页面出现的顺序加载,而多个async脚本是不能保证加载顺

浏览器加载 ES6 模块,也使用

异步加载,不会造成堵塞浏览器,即等到整个页面渲染完,再执行模块脚本,等同于打开了

你可能感兴趣的:(【ES6】Moudle)