参考文档:《深入理解js立即执行函数》
概述
很多时候我们需要创建一个私有的命名空间,该命名空间的变量和方法,不会破坏污染全局的命名空间。此时若是想访问全局对象,将全局对象以参数形式传进去即可,如jQuery代码结构:
1
2
3
| (function(window, undefined){
//jquery code
})(window);
|
匿名函数和表达式
在了解立即执行函数之前先明确一下函数声明、函数表达式及匿名函数的形式:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
| //函数声明
function test(){
}
//函数表达式
var test = function(){
}
//匿名函数
function(){
}
|
上面只有函数表达式是立即执行的。
立即执行函数
如果我们要使函数立即执行,就需要将该函数转换为函数表达式,转换的方式有下面几种:
1
2
3
| (function(test){
console.log(test);
})(123);
|
1
2
3
| (function(test){
console.log(test);
}(123));
|
1
2
3
| !function(test){
console.log(test);
}(123);
|
1
2
3
| +function(test){
console.log(test);
}(123);
|
1
2
3
| -function(test){
console.log(test);
}(123);
|
1
2
3
| var fn = functin(test){
console.log(test);
}(123);
|
模块化
Vue的框架立即执行函数如下,通过上面分析我们已经知道会立即执行 2,3,4 行,我们分析一下为什么下面执行到了 helloword.
1
2
3
4
5
6
7
| (function(global, factory){
typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
typeof define === 'function' && define.amd ? define(factory) :
(global = global || self, global.Vue = factory());
}(this, function(){
document.writeln("hello world");
}))
|
在 ES6 之前没有模块化的概念,现在 JavaScript 支持了 module, 分为导出 export 和导入 import 两个模块。
每一个文件就是一个模块,在文件中定义的变量,函数,对象在外部是无法获取的。如果你希望外部可以读取模块当中的内容,就必须使用 export 来对其进行暴露(输出)。
export 是用来定义模块的,可以导出对象、函数、类、字符串等等。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
| const a = 1;
let b = 2;
function show(){
console.log(10);
}
export { //暴露模块
a,
b as c,
show
}
show(); // 10
console.log(a); // 1
console.log(b); // 2
console.log(c); // c is not defined 因为在这个js里他还是b,只不过导出到另外一个文件里才叫c
|
1
2
3
4
5
6
7
| <script type="module">
import {a,c,show} from './1.js';
show(); // 10
console.log(a); // 1
console.log(c); // 2
console.log(b); // b is not defined 已经将导入的b更名为c,所以这里叫c
</script>
|
上面的例子中可以将export导出的内容通过as进行更名,import导入的也可以通过as改名。
1
2
3
4
5
6
| <script type="module">
import * as goto from './1.js'; // * 代表1.js中导出的全部的内容,但是不能直接输出*,必须改名
console.log(goto); // 整个json对象
console.log(goto.a); // 1
goto.show(); // 10
</script>
|
导出的方式还有另外一种:export default {}
和 export {}
的区别是前者导出的东西需要在导入的时候加 {}
,而后者则不需要。
1
2
3
4
5
| const a = 1;
const b = 2;
const c = 3;
export {a,b}
export default c;
|
1
2
3
4
| <script type="module">
import c,{a,b} from './1.js'; // 同时导入export和export default的时候,必须把默认的放在前面
console.log(a,b,c); // 1 2 3
</script>
|
还有一种动态引入方式,例如:
1
2
3
4
5
| <script type="module">
import('./1.js').then(res =>{
console.log(res.a); // 1
});
</script>
|
CommonJs规范
上面的 exports
命令实际上是 CommonJS
模块规范中的 module.exports
的缩写,在每个模块(文件)头部都有如下命令:
1
| var exports = module.exports;
|
而 module.exports
属性表示当前模块对外输出的接口,其他文件加载该模块,实际上就是读取 module.exports
变量。
Node 应用由模块组成,采用 CommonJS 模块规范。Node内部提供一个Module构建函数。所有模块都是Module的实例。
1
2
3
4
5
| function Module(id, parent) {
this.id = id;
this.exports = {};
this.parent = parent;
// ...
|
每个模块内部,都有一个module对象,代表当前模块。它有以下属性。
- module.id 模块的识别符,通常是带有绝对路径的模块文件名。
- module.filename 模块的文件名,带有绝对路径。
- module.loaded 返回一个布尔值,表示模块是否已经完成加载。
- module.parent 返回一个对象,表示调用该模块的模块。
- module.children 返回一个数组,表示该模块要用到的其他模块。
- module.exports 表示模块对外输出的值。
AMD规范
CommonJS规范加载模块是同步的,也就是说,只有加载完成,才能执行后面的操作。AMD规范则是非同步加载模块,允许指定回调函数。由于Node.js主要用于服务器编程,模块文件一般都已经存在于本地硬盘,所以加载起来比较快,不用考虑非同步加载的方式,所以CommonJS规范比较适用。但是,如果是浏览器环境,要从服务器端加载模块,这时就必须采用非同步模式,因此浏览器端一般采用AMD规范。
AMD规范使用define方法定义模块,下面就是一个例子:
1
2
3
4
5
6
7
8
9
| define(['package/lib'], function(lib){
function foo(){
lib.log('hello world!');
}
return {
foo: foo
};
});
|
AMD规范允许输出的模块兼容CommonJS规范,这时define方法需要写成下面这样:
1
2
3
4
5
6
7
8
9
10
11
12
| define(function (require, exports, module){
var someModule = require("someModule");
var anotherModule = require("anotherModule");
someModule.doTehAwesome();
anotherModule.doMoarAwesome();
exports.asplode = function (){
someModule.doTehAwesome();
anotherModule.doMoarAwesome();
};
});
|
所以Vue框架的入口代码实际上是判断了当前的模块化规范,最后根据对应模块规范将 factory
方法暴漏出去。
1
2
3
4
5
6
7
| (function(global, factory){
typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
typeof define === 'function' && define.amd ? define(factory) :
(global = global || self, global.Vue = factory());
}(this, function(){
document.writeln("hello world");
}))
|