🔥码云GVP开源项目 12k star Uniapp+ElementUI 功能强大 支持多语言、二开方便! 广告
## Module语法 > 1.概述 > 2.严格模式 > 3.export命令 > 4.import命令 > 5.整体加载 > 6.export default 命令 > 7.export 与 import 的复合写法 > 8.模块的继承 > 9.跨模块常量 ### 概述 > 历史上,JavaScript 一直没有模块(module)体系,无法将一个大程序拆分成互相依赖的小文件,再用简单的方法拼装起来。其他语言都有这项功能,比如 Ruby 的require、Python 的import,甚至就连 CSS 都有@import,但是 JavaScript 任何这方面的支持都没有,这对开发大型的、复杂的项目形成了巨大障碍 在 ES6 之前,社区制定了一些模块加载方案,最主要的有 CommonJS 和 AMD 两种,前者用于服务器,后者用于浏览器。ES6 在语言标准的层面上,实现了模块功能,而且实现得相当简单,完全可以取代 CommonJS 和 AMD 规范,成为浏览器和服务器通用的模块解决方案 ### 2.严格模式 ES6 的模块自动采用严格模式,不管你有没有在模块头部加上"use strict"; 严格模式主要有以下限制。 变量必须声明后再使用 函数的参数不能有同名属性,否则报错 不能使用with语句 不能对只读属性赋值,否则报错 不能使用前缀 0 表示八进制数,否则报错 不能删除不可删除的属性,否则报错 不能删除变量delete prop,会报错,只能删除属性delete global[prop] eval不会在它的外层作用域引入变量 eval和arguments不能被重新赋值 arguments不会自动反映函数参数的变化 不能使用arguments.callee 不能使用arguments.caller 禁止this指向全局对象 不能使用fn.caller和fn.arguments获取函数调用的堆栈 增加了保留字(比如protected、static和interface) 上面这些限制,模块都必须遵守。由于严格模式是 ES5 引入的,不属于 ES6,所以请参阅相关 ES5 书籍,本书不再详细介绍了。 其中,尤其需要注意this的限制。ES6 模块之中,顶层的this指向undefined,即不应该在顶层代码使用this。 ### 3.export命令 模块功能主要由两个命令构成:export和import。export命令用于规定模块的对外接口,import命令用于输入其他模块提供的功能。 一个模块就是一个独立的文件。该文件内部的所有变量,外部无法获取。如果你希望外部能够读取模块内部的某个变量,就必须使用export关键字输出该变量 ~~~ export var a = 'hello'; export var b = 'world'; export var c = 'es6'; ~~~ ~~~ export {a, b, c}; ~~~ ~~~ export function multiply(x, y) { return x * y; }; ~~~ ~~~ function v1() { ... } function v2() { ... } export { v1 as streamV1, v2 as streamV2, v2 as streamLatestVersion }; ~~~ 需要特别注意的是,export命令规定的是对外的接口,必须与模块内部的变量建立一一对应关系。 ~~~ // 报错 export 1; // 报错 var m = 1; export m; ~~~ ~~~ // 写法一 export var m = 1; // 写法二 var m = 1; export {m}; // 写法三 var n = 1; export {n as m}; ~~~ ~~~ // 报错 function f() {} export f; // 正确 export function f() {}; // 正确 function f() {} export {f}; ~~~ export语句输出的接口,与其对应的值是动态绑定关系,即通过该接口,可以取到模块内部实时的值 ~~~ export var foo = 'bar'; setTimeout(() => foo = 'baz', 500); ~~~ export命令可以出现在模块的任何位置,只要处于模块顶层就可以。如果处于块级作用域内,就会报错 ~~~ function foo() { export default 'bar' // SyntaxError } foo() ~~~ ### 4.import 命令 ~~~ import {firstName, lastName, year} from './profile.js'; function setName(element) { element.textContent = firstName + ' ' + lastName; } ~~~ ~~~ import { lastName as surname } from './profile.js'; ~~~ import命令输入的变量都是只读的,因为它的本质是输入接口。也就是说,不允许在加载模块的脚本里面,改写接口。 ~~~ import {a} from './xxx.js' a = {}; // Syntax Error : 'a' is read-only; ~~~ import命令具有提升效果,会提升到整个模块的头部,首先执行。 ~~~ foo(); import { foo } from 'my_module'; ~~~ 由于import是静态执行,所以不能使用表达式和变量,这些只有在运行时才能得到结果的语法结构。 ~~~ // 报错 import { 'f' + 'oo' } from 'my_module'; // 报错 let module = 'my_module'; import { foo } from module; // 报错 if (x === 1) { import { foo } from 'module1'; } else { import { foo } from 'module2'; } ~~~ import语句会执行所加载的模块,因此可以有下面的写法 ~~~ import 'lodash'; ~~~ 如果多次重复执行同一句import语句,那么只会执行一次,而不会执行多次。 ~~~ import 'lodash'; import 'lodash'; ~~~ 上面代码加载了两次lodash,但是只会执行一次。 ~~~ import { foo } from 'my_module'; import { bar } from 'my_module'; // 等同于 import { foo, bar } from 'my_module'; ~~~ ### 5.模块的整体加载 ~~~ export function area(radius) { return Math.PI * radius * radius; } export function circumference(radius) { return 2 * Math.PI * radius; } ~~~ 加载模块 ~~~ import { area, circumference } from './circle'; console.log('圆面积:' + area(4)); console.log('圆周长:' + circumference(14)); ~~~ 也可以 ~~~ import * as circle from './circle'; console.log('圆面积:' + circle.area(4)); console.log('圆周长:' + circle.circumference(14)); ~~~ 不允许运行时改变 ~~~ import * as circle from './circle'; // 下面两行都是不允许的 circle.foo = 'hello'; circle.area = function () {}; ~~~ ### 6.export default命令 从前面的例子可以看出,使用import命令的时候,用户需要知道所要加载的变量名或函数名,否则无法加载。但是,用户肯定希望快速上手,未必愿意阅读文档,去了解模块有哪些属性和方法。 为了给用户提供方便,让他们不用阅读文档就能加载模块,就要用到export default命令,为模块指定默认输出。 ~~~ export default function () { console.log('foo'); } ~~~ ~~~ // import-default.js import customName from './export-default'; customName(); // 'foo' ~~~ export default命令其实只是输出一个叫做default的变量,所以它后面不能跟变量声明语句 ~~~ // 正确 export var a = 1; // 正确 var a = 1; export default a; // 错误 export default var a = 1; ~~~ ### 7.export 与 import的复合写法 如果在一个模块之中,先输入后输出同一个模块,import语句可以与export语句写在一起。 ~~~ export { foo, bar } from 'my_module'; // 可以简单理解为 import { foo, bar } from 'my_module'; export { foo, bar }; ~~~ ~~~ // 接口改名 export { foo as myFoo } from 'my_module'; // 整体输出 export * from 'my_module'; ~~~ ~~~ export { es6 as default } from './someModule'; // 等同于 import { es6 } from './someModule'; export default es6; ~~~ ### 8.模块的继承 ~~~ // circleplus.js export * from 'circle'; export var e = 2.71828182846; export default function(x) { return Math.exp(x); } ~~~ ### 9.跨模块常量 ~~~ // 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 ~~~ ~~~ // constants/db.js export const db = { url: 'http://my.couchdbserver.local:5984', admin_username: 'admin', admin_password: 'admin password' }; // constants/user.js export const users = ['root', 'admin', 'staff', 'ceo', 'chief', 'moderator']; ~~~ ### 课后习题 1.下面的代码有什么问题? ~~~ export 2; var x = 3; export x; ~~~ ~~~ export default var a = 1; ~~~ ~~~ import {x} from 'xx.js'; x = []; ~~~ ~~~ import { 'a' + '1'} form 'xxx.js'; ~~~ 2.编写一个js模块。模块里导出加减乘除这四种方法。