模块化
模块是实现特定功能的一组方法,模块化是一种规范、一种约束,这种约束会大大提升开发效率。JS模块化思想是将每个JS文件看作是一个模块,每个模块通过固定的方式引入,并且通过固定的方式向外暴露指定的内容。
函数封装
function fn1(){
statement
}
function fn2(){
statement
}
对象
var myModule = {
var1: 1,
var2: 2,
fn1: function(){
},
fn2: function(){
}
}
AMD和RequireJS
RequireJs是JS模块化的工具框架,是AMD规范的具体实现。但是有意思的是,RequireJs诞生之后,推广过程中产生的AMD规范。RequireJs的优点:1)动态并行加载js,依赖前置,无需再考虑js加载顺序问题;2)核心还是注入变量的沙箱编译,解决模块化问题;3)规范化输入输出,使用起来方便;4)对于不满足AMD规范的文件可以很好地兼容。
require方式(AMD模式)
test.js
// 首先使用define进行定义
define(function(){
function fun1(){
alert("it works");
}
fun1();
})
// 在页面中使用require进行引用
require(["js/a"]);
requireJs 的问题在于,加在一个模块时,会预先加载该模块的所有依赖模块,但是这些依赖很可能一开始并不用到。同时依赖写起来一长串,也很麻烦。比较好的是 AMD 保留了 commonJs 中的 require、exprots、module3 个功能,可以不把以来都写在 dependencies 中,而是在需要时使用 require 引入。
AMD是’‘AsynchronousModuleDefinition’‘的缩写,意思就是’‘异步模块定义’’。它采用异步方式加载模块,模块的加载不影响它后面语句的运行。所有依赖这个模块的语句,都定义在一个回调函数中,等到所有依赖加载完成之后(前置依赖),这个回调函数才会运行。
define() 函数
AMD规范只定义了一个函数 define
,它是全局变量。函数的描述为:
define([module-name?],[array-of-dependencies?],[module-factory-or-object]);
- module-name:模块标识,可以省略
- array-of-dependencies:所依赖的模块数组,可以省略
- module-factory-or-object:模块的实现或者一个 JavaScript 对象
define 函数具有异步性,其首先会异步加载第二个参数中列出的依赖模块,当所有的模块被加载后,执行第三个参数的回调函数。
define 函数使用
define(function(){
var a = require("A");
})
define函数在执行的时候,会调用函数的 toString 方法,并扫描其中的 require 调用,提前载入这些模块,载入完成后再执行。
define("A", ["require", "exports"], function(require, exports){
export.fun = function(){
return require("B").fun;
}
});
require 函数是用来模块依赖,即获取模块的引用,即使模块没有作为参数定义,也能够被使用;exports 定义 A 模块实体,在其上定义的任何属性和方法就是 A 模块的属性和方法。通过 exports.fun= … 就是为 A 模块定义了一个 fun 方法。
CMD
CMD规范说明:
Common Module Definition(通用模块定义)
专用于浏览器端,模的加载是异步的
模块使用时才会加载执行
CMD基本语法:
定义暴露模块:
//定义没有依赖的模块
define(function(require, exports, module){
exports.xxx = value
module.exports = value
})
//定义有依赖的模块
define(function(require, exports, module){
//引入依赖模块(同步)
var module2 = require('./module2')
//引入依赖模块(异步)
require.async('./module3', function (m3) {
})
//暴露模块
exports.xxx = value
})
引入使用模块:
define(function (require) {
var m1 = require('./module1')
var m4 = require('./module4')
m1.show()
m4.show()
})
sea.js
Sea.js 追求简单、自然的代码书写和组织方式,具有以下核心特性:
Sea.js 还提供常用插件,非常有助于开发调试和性能优化,并具有丰富的可扩展接口。
加载模块
SeaJS
提供了seajs.use
来加载模块
语法:
seajs.use([dependencies], function(){})
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title></title>
</head>
<body>
<script src="sea.js"></script>
<script src="cmd2.js"></script>
<script src="cmd1.js"></script>
<script type="text/javascript">
seajs.use(['cmd2.js','cmd1.js'],function(cmd2,cmd1){
// ....
})
</script>
</body>
</html>
CommonJS
浏览器不兼容CommonJS的根本原因,在于缺少四个Node.js环境的变量。
- module
- exports
- require
- global
只要能够提供这四个变量,浏览器就能加载 CommonJS 模块。
下面是一个简单的示例。
var module = { exports: {} }; (function(module, exports) { exports.multiply = function (n) { return n * 1000 }; }(module, module.exports)) var f = module.exports.multiply; f(5) // 5000
上面代码向一个立即执行函数提供 module 和 exports 两个外部变量,模块就放在这个立即执行函数里面。模块的输出值放在 module.exports 之中,这样就实现了模块的加载。
CommonJS规范规定,每个模块内部,module
变量代表当前模块。这个变量是一个对象,它的exports
属性(即module.exports
)是对外的接口。加载某个模块,其实是加载该模块的module.exports
属性。
var x = 5;
var addX = function (value) {
return value + x;
};
module.exports.x = x;
module.exports.addX = addX;
上面代码通过module.exports
输出变量x
和函数addX
。
CommonJS模块的特点如下。
- 所有代码都运行在模块作用域,不会污染全局作用域。
- 模块可以多次加载,但是只会在第一次加载时运行一次,然后运行结果就被缓存了,以后再加载,就直接读取缓存结果。要想让模块再次运行,必须清除缓存。
- 模块加载的顺序,按照其在代码中出现的顺序。
ES6 模块化
es6模块基本用法
es6中新增了两个命令export和import,export命令用于规定es6模块的对外接口,import 命令用于输入其他es6模块化提供的功能。
一个es6模块就是一个独立的文件。该文件内部的所有变量,外部无法获取。如果你希望外部能够读取模块内部的某个 变量, 就必须使用export关键字输出该变量。下面是一个JS文件,里面使用export命令输出变量。
export const add = function (a, b) {
return a + b
}
export const subtract = function (a, b) {
return a - b
}
使用export命令定义了模块的对外接口以后,其他JS文件就可以通过import命令加载这个模块(文件)。
import { add, subtract } from './test.js'
add(1, 2)
substract(3, 2)
es6 export详细用法
export 不止可以导出函数,还可以导出对象,类,字符串等等
export const obj = {
test1: ''
}
export const test = ''
export class Test {
constructor() {
}
}
export的写法,除了像上面这样,还有另外一种。
let a = 1
let b = 2
let c = 3
export {
a,
b,
c
}
上面代码在export命令后面,使用大括号指定所要输出的一组变量。它与前一种写法是等价的,但是应该优先考虑使用这种写法。因为这样就可以在脚本尾部,一眼看清楚输出了哪些变量。
通过 as 改变输出名称
// test.js
let a = 1
let b = 2
let c = 3
export {
a as test,
b,
c
}
import { test, b, c} from './test.js'
上面啊的写法中,import 中需要指定加载的变量名或函数名,否则无法加载。但是,用户肯定希望快速上手,未必愿意阅读文档,去了解模块有哪些属性和方法。
export default 指定默认输出, import 无需知道变量名就可以直接使用
export default function () {
console.log('hello world')
}
import say from './test.js'
say()
有了export default命令,加载模块时就非常直观了,以一些常用的模块为例
import $ from 'jQuery'
import _ from 'lodash'
import moment from 'moment'