模块化
就是把代码分成一块一块的,每个块即使有相同的变量名也不会冲突(没有全局污染),每个块还可以对外暴露变量,提供给其他模块使用。
其意义就是避免产生一堆全局变量,还能使逻辑结构更加清晰。
实现原理:
把每个模块的代码都放在一个函数中执行,这样就避免了全局污染
把每个模块需要暴露的变量,统一挂载到一个全局变量。加载变量时,就从这个全局变量去取值。
举个例子:
在没有模块化这个概念时,我们的代码是这样,所有的代码都写在一个脚本里面。
<script>
var desc = '这里提供了几个函数';
console.log(desc);
function add(a, b) {
return a + b
}
console.log(add(1, 2))
</script>
当代码量太大时,非常不好维护,另外还有性能问题。那么改进一下:
<script>
var desc = '这里提供了一些函数';
console.log(desc);
function add(a, b) {
return a + b
}
</script>
<script>
console.log(add(1, 2))
</script>
这时候,函数的提供和调用逻辑分开了,其实就可以算是模块化了,可维护性这个问题是解决了。
但是还有个问题依然没有解决:全局污染。
代码里所有的变量,都是在全局上下文上声明的,在整个程序运行期间,都不会被回收。比如变量desc 、add。不仅造成内存泄漏、而且容易命名冲突。
那么再改进一下:
<script>
// 同一挂载模块变量的全局变量
var modules = { };
modules.require = function (moduleId) {
return modules[moduleId].exports;
}
</script>
<script>
(function() {
var module = { id: 'module1', exports: {} };
modules.module1 = module;
(function(exports, require, module) {
var desc = '这里提供了几个函数';
console.log(desc);
function add(a, b) {
return a + b
}
module.exports = {add: add}
})(module.exports, modules.require, module )
})()
</script>
<script>
(function() {
var module = { id: 'module2', exports: {} };
modules.module2 = module;
(function(exports, require, module) {
var add = require('module1').add;
console.log(add(1, 2))
})(module.exports, modules.require, module )
})()
</script>
这就已经实现模块化了,只不过是否真的要这样手动引入很多个script,还可以再优化。比如在node中可以使用node的 IO 能力,结合 eval 函数去加载模块并执行代码。在浏览器中可以执行一个入口文件,然后通过动态添加 script 标签去加载模块并执行代码。
CommonJS规范
一个文件就是一个模块,加载时会执行这个文件,且加载同步化的。执行文件时,会自动用上运行时。这个运行时就是一个自执行的匿名函数,并会传递一些参数进来。如下,可以打印出运行时传递的参数。
// test.js
console.log(arguments)
使用exports或者module.exports导出变量
使用require()导入变量,已经被reuiqre()加载过的模块,其导出结果会被缓存。
AMD\CMD规范
都是异步加载模块,且AMD本身就是async module define的简写。分别是RequireJS、SeaJs(淘宝)在推广过程中对模块定义的规范化产出。
- 全局函数 difine,用于定义一个模块
- 全局函数 require,用于加载一个模块
指定了入口文件后,被应用到的模块会被加载,加载完成之后,执行回调函数。至于如何判断是否加载并执行完毕,用的是script的load事件。下面这句话是我在 requirejs 源码中找到的注释。
IE9 has a subtle issue in its addEventListener and script onload firings that do not match the behavior of all other browsers with addEventListener support, which fire the onload event for a script right after the script execution.
翻译一下:IE9 的 addEventListener 和 script 标签的 load 的触发有个不明显的问题,其他浏览器会在脚本执行后才触发 load事件,这点上 IE9 和他们不同。
这表明,其他浏览器下,可以监听script标签的load事件。requirejs源码中也确实是这么做的。
UMD规范
umd规范,是一种糅合,判断当前环境支持哪种模块化,是amd还是commonjs,或是都不支持(如浏览器又未使用amd),然后根据具体情况来实现模块化。其原理如下:
(function (root, factory) {
if (typeof define === 'function' && define.amd) {
// AMD
define(['jquery'], factory);
} else if (typeof exports === 'object') {
// Node, CommonJS-like
module.exports = factory(require('jquery'));
} else {
// Browser globals (root is window)
root.returnExports = factory(root.jQuery);
}
}(this, function ($) {
// methods
function myFunc(){};
// exposed public method
return myFunc;
}));
ES6
导入变量
命名导入
import { sth } from 'module.js';
import { sth as something } from 'module.js';
命名空间导入
import * as sth from 'module.js';
默认导入
import sth from 'module.js';
空导入(仅仅执行模块代码,但是不输入任何值)
import 'module.js';
导出变量
命名导出
export { sth } from 'module.js';
export { sth as something } from 'module.js';
默认导出
export default sth from 'module.js';
动态导入
import()函数
网友评论