日常的学习笔记,包括 ES6、Promise、Node.js、Webpack、http 原理、Vue全家桶,后续可能还会继续更新 Typescript、Vue3 和 常见的面试题 等等。
模块化与全局对象
首先,模块化包括 require() 、exports 和 module 等。
console.log(global.exports); // undefined
console.log(global.module); // undefined
console.log(global.require); // undefined
但是当我们在控制台打印时,发现其结果都是 undefined。我们却可以直接对这些属性进行访问,比如console.log(require)
。
这里需要注意,global上有的属性叫做全局属性,可以直接访问。但 require() 、exports 和 module 也可以直接访问,但是他们却不在global对象上。
每个文件都是一个模块,模块化的实现借助的是函数。
而这个函数里面有五个参数,分别是 __dirname、__filename 、require() 、exports 和 module。(后续我会写一篇文章来实现模块化)
模块化的规范
模块化规范包括以下几种
- CommonJs规范
- ESModule规范
- AMD
- CMD
- UMD
- SystemJs
- ...
为什么要有模块化规范?
JS 诞生的时候,仅仅是为了实现网页表单的本地校验和简单的 dom 操作处理。所以并没有模块化的规范设计。
项目小的时候,我们可以通过命名空间、局部作用域、自执行函数等手段实现变量不冲突。但是到了大一点的项目,各种组件,各种第三方插件和各种 js 脚步融合的时候,就会发现这些技巧远远不够。
单例模式
最早的时候,一些项目组会采用 单例设计模式 来解决这个问题。
// 成员a
var a = {
aa(){
}
// do something...
}
// 成员b
var b = {
bb(){
}
// do something...
}
这种解决方式就会出现 命名过长,难以调用 等问题。所以他 解决了,但没有完全解决。
AMD、CMD
为了解决模块化的问题,人们用 文件拆分 的方式,配合 iife **自执行函数 **来解决。也就是 AMD、CMD。
let xx = (function(){
// ...
return obj
})()
这种方式在前端中需要请求顺序,也就出现了依赖问题。比如我们现在有一个文件a,他需要依赖文件b和文件c,但是c文件又需要依赖文件d。这时候就出现了文件之间的依赖问题,我们并不知道每个文件需要依赖的文件都有谁。
这就是以前出现的依赖前置 define(['jquery','xxx'],function(){ // ... })
UMD
它可以通过运行时或者编译时让同一个代码模块在使用 CommonJs、CMD 甚至是 AMD 的项目中运行。未来同一个 JavaScript 包运行在浏览器端、服务区端甚至是 APP 端都只需要遵守同一个写法就行了。
简单来说,他的出现就是为了兼容CommonJs、CMD和AMD,但是他并不兼容ESModule。(所以我们在平时发布组件库时,就会将代码打包成 UMD 和 ESModule 两种)
CommonJs和ESModule的区别
CommonJs 和 ESModule 的定义都是一样的,一个文件就是一个模块。
- CommonJs的用法: require() 是使用其他模块,module.exports 是导出模块。
- ESModule的用法:import 是使用其他模块,export 是导出模块。
CommonJs 是基于Node的I/O操作,如果我想进行模块的引入和导出,我就需要使用Node来进行操作。
而 ESModule 是基于浏览器的请求,如果我想进行引入导出的操作,我就需要使用浏览器。
我们也可以理解为,ESModule 是静态的,而 CommonJs是动态的。所以我们平时才会使用 require() 来对资源进行动态引入。 (注:目前ES7中支持 import('./xxx')来对资源进行动态引入)
// ESModule不可以动态引入资源
if(true){
// 这种写法不成立
import xxx from './xxx'
}
// CommonJs可以动态引入资源
if(true){
require('./xxx')
}
(在现在的实际项目中,我们一般会使用webpack来对文件进行自动编译。)
本篇文章由莫小尚创作,文章中如有任何问题和纰漏,欢迎您的指正与交流。
您也可以关注我的 个人站点、博客园 和 掘金,我会在文章产出后同步上传到这些平台上。
最后感谢您的支持!