随着javascript的发展,社区产生了很多模块标准。(例如CommonJS、ES6 Module)等等
1. CommonJS 2009年提出包括模块、文件、io、控制台在内的一些列标准(node.js中采用了CommonJS标注的一部分)
(1)script标签与封装CommonJS的区别在于前者的顶层作用域是全局作用域,在进行变量及函数声明时会污染全局环境,而后者会形成一个属于模块自身的作用域,所有的变量及函数只有自己能访问,对外是不可见的。(CommonJS标准引入js是通过require函数)
例如:// test.js
var name = '哈哈';
// index.js
var name = '呵呵';
require('./test.js');
console.log(name); // 呵呵
(2)CommonJS导出写法 (是一个模块向外暴露自身的唯一方法)
module.exports = {
name: 'calculater',
add: function(a,b){
return a + b;
}
}
或者
exports.name = 'calculater';
exports.add = function(a, b){
return a + b;
};
注意两种方式不恰当的把module.exports 与 exports 混用会导致前一个导出属性的对象丢失
例如:
exports.add = function(a, b){
return a + b;
};
module.exports = {
name: 'calculater'
}
(上面的代码想通过exports导出了add属性,然后将module.exports重新赋值为另一个对象,这样会导致原本拥有add属性的对象丢失了,最后导出的只有name)
* 需要注意的一点 建议将module.exports 以及 exports语句放在整个js模块的最后(因为module.exports 以及 exports的后面还可以继续追加代码执行,这种写法不建议)
function test(){
console.log('123');
}
module.exports = {
test: test
};
2. ES6 Module
//calculator.js
export default {
name: 'calculator',
add: function(a, b){
return a + b;
}
}
//index.js
import calculator from './calculator.js';
const sum = calculator.add(2,3);
console.log(sum);
3.CommonJS 与 ES6 module 的区别
(1)commonjs 是动态模块结构,它的依赖关系的建立发生在代码运行阶段
es6 module 是静态模块结构,它的依赖关系的建立发生在代码编译阶段
(2)commonjs的优点是能在require的时候通过动态通过if语句判定要执行加载某个模块
(3) es6 module的优点是 :
a: 死代码检测,通过静态分析在打包的时候去掉模块为使用的接口、组建等等,以减小打包资源的体积
b: 模块变量类型检测,由于javascript属于动态语言在代码执行之前是不会检测类型错误,而es6 module的静态模块结构有助于确保模块之间传递的值或者接口类类型是正确的
c: 编译器优化,commonjs本质上导入的是一个对象,而es6 module支持直接导入变量,减少了引用层级,程序效率更高
(4)
commonjs 导入模块时是值拷贝
var count = 0;
module.exports = {
count: count,
add: function(a, b){
count += 1;
return a + b;
}
}
//index.js
var count = require('./calculator.js').count;
var add = require('./calculator.js').add;
console.log(count); // 0
add(2, 3);
console.log(count); // 0 依旧未变 说明变量值的改变对拷贝值不会造成影响
count += 1;
console.log(count); // 1 (拷贝值可以更改)
es6 module 是动态映射(并且这个映射是只读的)
//calculaotor.js
let count = 0;
const add = function(a, b){
count += 1;
return a + b;
}
export { count, add };
// index.js
import { count, add } from './calculator.js';
console.log(count); // 0
add(2, 3);
console.log(count); // 1 可以看出实时映射count值的变化
// count += 1; //不可更改,会抛出 count is read-only
(5)ES6 module 可以更好地支持循环依赖
4. 加载npm模块
npm作为包管理器可以帮助开发这快速引入一些工具接口(例如url处理、日期解析等等)
javascript 主流的包管理器有 npm 和 yarn
我们的本地工程安装和加载一个外部的npm模块呢?(下面以lodash这个库为例)
(1) 项目初始化 npm init -y
(2) 安装lodash npm install lodash
此时会讲lodash安装在工程的node_modules目录下,并将对该模块的依赖信息记录在package.json中
a: 在使用时,加载一个npm模块的方式很简单,只要引入包的名字即可。
//index.js
import _ from 'lodash';
b: 引用npm模块中内部的某个JS
import all from 'lodash/fp/all.js';
console.log('all', all);
*: 每个npm模块都有一个入口,实际上就是加载该模块的入口文件,这个入口的被维护在模块内部的package.json的main字段中,当加载模块lodash 时,实际上加载的是 node_modules/lodash/lodash.js
总结:
commonjs和es6 module 是目前主流的模块标准,它们的区别在于前者建立模块关系是在运行时,后者是在编译时;
模块导入方面:commonjs导入的是拷贝值,es6 module 导入的是只读的变量映射;
es6 module通过静态特写可以进行编译过程中的优化,并且具备处理循环依赖的能力