在一个项目中,可能同一个功能会在不同的地方用到过,为了节约开发成本,于是我们主张模块化开发。
这样不但使重复的功能可以直接复用,而且避免了多人协作开发时引入同名变量造成错误的尴尬。
函数模块
在js中有这样的概念,主张语义化开发,尽可能少的暴露全局变量。这样做的原因是:
(1)避免全局变量污染。在同一个文件里面定义多个同名的变量可能会导致变量覆盖,以致于调用的方法错误。
(2)优化代码性能。浏览器在处理时,会把定义在全局的变量挂载到window的属性下,每次查找变量时,会依次查找window下的属性,过多的全局变量会导致查找变量的时间越来越长。
所以在js中就有了用函数来封闭作用域的现象。
函数模块就是把要实现的功能封闭在一个函数里,最后导出一个接口来供外部环境调用。他需要满足以下两个条件:
(1)必须有外部的封闭函数;
(2)封闭函数必须返回至少一个内部函数。
具体的举例如下:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19function func () {
var n = 1;
var arr = [8, 5, 6]
function add() {
n += 1;
console.log(n);
}
function arrSort() {
console.log(arr.sort());
}
return {
add: add,
arrSort: arrSort
}
}
var foo = func();
foo.add(); //2
foo.arrSort(); //[5, 6, 8]
这个返回的对象中含有对内部函数的引用。内部数据变量仍然是隐藏的私有状态(只有函数内部可以被访问到)。
在这里将返回的对象赋值给foo, 因此就可以通过访问对象属性名的方式来访问到函数里面方法。
从模块中返回一个对象并不是必须的,也可以直接返回一个内部函数。
上面是通过函数调用的方式将返回的对象赋值给一个外部变量来调用执行。该方法可以被调用多次,执行多次。
如果只是想只执行一遍函数的运算过程,保留函数的运行结果来调用的话,可以选择立即执行函数。
代码修改如下:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18var foo = (function () {
var n = 1;
var arr = [8, 5, 6]
function add() {
n += 1;
console.log(n);
}
function arrSort() {
console.log(arr.sort());
}
return {
add: add,
arrSort: arrSort
}
})()
foo.add(); //2
foo.arrSort(); //[5, 6, 8]
当想要给函数传参时,也是像以前立即函数正常的处理逻辑。
ES6的模块
通过上面的内容我们了解到,所谓模块大概就是封装一个接口,导出一个变量(接口)来实现的。在ES6中也是一样,只不过可能导入导出的方式发生了改变。
ES6中的模块必须被定义在一个独立的文件中(一个文件一个模块),浏览器在加载时可以同步的导入模块。
具体举例如下:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21//myInform.js文件
function person (name) {
return name;
}
export person;
//sayHello.js文件
import person from "myInform";
var myName = 'fs';
function hello () {
console.log('Hello ' + person(myName));
}
export hello;
//all.js文件
module myInform from "myInform";
module sayHello from "sayHello";
console.log(myInform.person('ll')); //ll
sayHello.hello(); //Hello fs
import可以将模块中的一个或多个API导入到当前的作用域中,并分别绑定在一个变量上(person)。
module会将整个模块的API导入并绑定到一个变量上(myInform, sayHello)。
export会将当前模块的一个接口(变量,函数)导出为公共API。
函数模块和ES6模块的区别
基于上述两种形式,考虑到ES6是JS的升级版,所以肯定是在函数模块的基础上进行了优化。
具体区别如下:
函数模块只有在函数运行时里面的代码才会被考虑进来,因此可以在代码运行时修改代码的API。
ES6模块在编译时会被导入模块的API检查是否真的存在。如果导入的API不存在,则编译器直接报错。