起初,项目的代码量不大时,常把所有代码写在一个文件中。随着代码量的不断增加,这样做会带来两个问题:一、不利于分工合作;二、不利于后期维护。因此,需要依据代码功能把代码从一个文件拆分到不同的文件。但是,现在又产生一个新的问题,多个文件的变量都位于全局作用域中,很容易产生冲突,为了解决这个问题,就有了模块。
最初,ES标准中还没有模块和模块系统的支持,只能通过立即执行函数来模拟模块的功能。具体做法是:把不同文件中的代码都放在立即执行函数中,然后把可能会被其他代码使用的变量、函数、类通过全局对象的属性暴露出去。如下代码所示:
(function() {
class Person {
constructor(name, age) {
this.name = name;
this.age = age;
}
}
window.Person = Person;
})();
通过立即执行函数模拟的模块,大大减少了全局变量,但是依然存在不少通过全局对象属性暴露的变量,例如,window.Person = Person;
,更大的问题是,我们需要手动管理加载文件的顺序,例如,文件A中的类继承了文件B中的类,那么在HTML文件中,
必须先于
引入,如果存在大量的复杂的依赖关系,手动管理依赖是非常困难的。这是立即执行函数模拟(实现)模块存在的两个主要问题。
下文中介绍的ES6中引入的模块和模块系统,会解决立即执行函数模拟模块存在的问题~~
1、 模块
模块是局部作用域代码块
在ES6中一个模块本质是一个文件
2、模块系统
模块系统主要解决问题:
以前,ES还没有提供模块的原生支持时,第三方JS库,例如,RequireJS、SeaJS等实现模块系统的功能。
在HTML代码中引入一个模块的方式
<script src="file.js" type="module">script>
比起一般的引入方式,多了一个type属性,属性值module表示以模块的方式引入文件。如果不添加属性"type="module,那么,文件以普通的方式引入,文件中的变量会作为全局变量处理。
什么时候或场景需要把文件以模块方式引入呢?
当文件中出现import或export关键字时。
一个模块没有导出,也可以导入。只是将模块中的代码执行一遍,没有暴露任何变量
1、export default 导出和对应的import导入
export default 表达式;
表达式:基本类型的字面量,变量,函数和类的表达式。函数和类表达式可以匿名。
export default 1;
export default function(){};
export default class{}
const str = 'hello';
export default str;
一个模块只能定义一个exprot default,不能导出多个。
export default 1;
export default 'hello';
会出现报错:
2、exprot导出和对应的improt导入语法
基本语法
方式一:export 声明语句;
export let/const/var variable = value;
export function foo(){}
exprot class Clz{}
方式二:先声明后export{变量名}
let variable = value;
exprot {variable};
注意 普通变量、函数和类表达式不能匿名。不能是基本类型值。
import {variable} from XXX.js
导出导入多个
利用基本语法方式二
const variable = value;
function foo() {}
class Clz {}
// 导出多个
export {variable, foo, Clz};
导出导入起别名
export {variable as username}
导入的”角色“的名字和我当前代码冲突,就可以别名
import {Clz as Person}
整体导入
包括export default导出的角色
import * as obj from 'module.js';
同时导入
import {variable, foo, Clz} from ‘module.js’
import def from ‘module.js’
export和exprot default可以同时导出,不过export default必须在最前面先导出,
import def, {variable, foo, Clz } from ‘module.js’
也可以分开导入
import def from ‘module.js’
import {variable, foo, Clz } from ‘module.js’
想法
export 对标Java中的public
没有export 对标Java中的private
1、模块本身作用域中的this指向undefined
应用,可以在文件首部添加以下代码,来提示用户应该以为模块的方式应用文件
if (typeof this !== 'undefined') {
throw new Error('请以模块方式引入该文件');
}
2、 import关键字和import()函数
import和export 命令会先于其他代码执行,和它所在的位置无关。因此,我们应该把它们放在模块的首部,而不是代码块,例如if代码块中。
import() 可以实现条件导入,即满足某些条件,例如,是否是PC端、移动端,而导入不同的模块。
此函数不是标准,只是提案。可以配合webpack解决兼容性问题。
3、 导入导出的复合写法
表达式包括基本类型值字面量或变量,函数、类表达式,可以匿名
声明即变量的声明;语句即表达式,包括变量,函数或者类的声明。