https://www.cnblogs.com/libin-1/p/7127481.html
为什么有模块概念
理想情况下,开发者只需要实现核心的业务逻辑,其他都可以加载别人已经写好的模块。
但是,Javascript不是一种模块化编程语言,在es6以前,它是不支持”类”(class),所以也就没有”模块”(module)了。
require时代
Javascript社区做了很多努力,在现有的运行环境中,实现”模块”的效果。
原始写法
模块就是实现特定功能的一组方法。
只要把不同的函数(以及记录状态的变量)简单地放在一起,就算是一个模块。
1
2
3
4
5
6
|
function m1(){
//...
}
function m2(){
//...
}
|
上面的函数m1()和m2(),组成一个模块。使用的时候,直接调用就行了。
这种做法的缺点很明显:”污染”了全局变量,无法保证不与其他模块发生变量名冲突,而且模块成员之间看不出直接关系。
对象写法
为了解决上面的缺点,可以把模块写成一个对象,所有的模块成员都放到这个对象里面
1
2
3
4
5
6
7
8
9
|
var module1 = new Object({
_count : 0,
m1 : function (){
//...
},
m2 : function (){
//...
}
});
|
上面的函数m1()和m2(),都封装在module1对象里。使用的时候,就是调用这个对象的属性
1
|
module1.m1();
|
这样的写法会暴露所有模块成员,内部状态可以被外部改写。比如,外部代码可以直接改变内部计数器的值。
1
|
module._count = 1;
|
立即执行函数写法
使用”立即执行函数”(Immediately-Invoked Function Expression,IIFE),可以达到不暴露私有成员的目的
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
var module = (function() {
var _count = 0;
var m1 = function() {
alert(_count)
}
var m2 = function() {
alert(_count + 1)
}
return {
m1: m1,
m2: m2
}
})()
|
使用上面的写法,外部代码无法读取内部的_count变量。
1
|
console.info(module._count); //undefined
|
module就是Javascript模块的基本写法。
立即函数的写法:
立即执行函数的写法,即IIFE等设计模式。这种函数在函数定义的地方就直接执行了。
理解IIFE设计模式的关键是要认识到,在ES6之前,JavaScript仅具有函数作用域(因此缺少块作用域),并通过闭包内部的引用传递值。ES6之后情况已不再如此,因为JavaScript的ES6版本使用let和const关键字实现了块作用域。
(function(){})()是匿名函数,主要利用函数内的变量作用域,避免产生全局变量,影响整体页面环境,增加代码的兼容性。
(function(){})是一个标准的函数定义,但是没有赋值给任何变量。所以是没有名字的函数,叫匿名函数。没有名字就无法像普通函数那样随时随地调用了,所以在他定义完成后就马上调用他,后面的括号()是运行这个函数的意思。
如下几种写法作用是相同的:
!function () { /* ... */ }();
~function () { /* ... */ }();
-function () { /* ... */ }();
+function () { /* ... */ }();
void function () { /* ... */ }();
(function (){/*...*/}());
(function (){/*...*/})();
所以:!function(){}()写法和(function(){})()是相同的
!function(){}()
①函数后的();表示要执行这个函数;
②而();要求前面必须是一个表达式。(最后有js表达式的定义)。
③所以“!”的作用是将function(){}函数体转为一个函数表达式。