模块化
涉及⾯试题:
- 为什么要使⽤模块化?
- 都有哪⼏种⽅式可以实现模块化,各有什么特点?
使⽤⼀个技术肯定是有原因的,那么使⽤模块化可以给我们带来以下好处:
· 解决命名冲突
· 提供复⽤性
· 提⾼代码可维护性
⽴即执⾏函数
在早期,使用 立即执⾏函数 实现模块化是常⻅的⼿段,通过函数作⽤域解决了命名冲突、污染全局作⽤域的问题。
(function(globalVariable){
globalVariable.test = function() {}
// ... 声明各种变量、函数都不会污染全局作⽤域
})(globalVariable)
AMD 和 CMD
鉴于⽬前这两种实现⽅式已经很少⻅到,所以不再对具体特性细聊,只需要了解这两者是如何使⽤的。
// AMD
define(['./a', './b'], function(a, b) {
// 加载模块完毕可以使⽤
a.do()
b.do()
})
// CMD
define(function(require, exports, module) {
// 加载模块
// 可以把 require 写在函数体的任意地⽅实现延迟加载
var a = require('./a')
a.doSomething()
})
CommonJS
--- CommonJS 最早是 Node 在使⽤,⽬前也仍然⼴泛使⽤,⽐如在 Webpack 中你就能⻅到它,当然⽬前在 Node 中的模块管理已经和 CommonJS 有⼀些 区别了。
// a.js
module.exports = { a: 1 }
// or
exports.a = 1
// b.js
var module = require('./a.js')
module.a // -> log 1
ar module = require('./a.js')
module.a
// 这⾥其实就是包装了⼀层⽴即执⾏函数,这样就不会污染全局变量了,
// 重要的是 module 这⾥,module 是 Node 独有的⼀个变量
module.exports = { a: 1 }
// module 基本实现
var module = {
id: 'xxxx', // 我总得知道怎么去找到他吧
exports: {} // exports 就是个空对象
}
// 这个是为什么 exports 和 module.exports ⽤法相似的原因
var exports = module.exports
var load = function (module) {
// 导出的东⻄
var a = 1
module.exports = a
return module.exports
};
// 然后当我 require 的时候去找到独特的
// id,然后将要使⽤的东⻄⽤⽴即执⾏函数包装下,over
- 另外虽然 exports 和 module.exports ⽤法相似,但是不能对 exports 直接赋值。
- 因为 var exports = module.exports 这句代码表明了 exports 和 module.exports 享有相同地址,通过改变对象的属性值会对两者都起效,
- 但是如果直接对 exports 赋值就会导致两者不再指向同⼀个内存地址,修改并不会对 module.exports 起效。
ES Module --- ES Module 是原⽣实现的模块化⽅案,与 CommonJS 有以下⼏个区别
-
CommonJS ⽀持动态导⼊,也就是 require(${path}/xx.js) ,后者⽬前不⽀持,但是 已有提案。
-
CommonJS 是同步导⼊,因为⽤于服务端,⽂件都在本地,同步导⼊即使卡住主线程影响 也不⼤。⽽后者是异步导⼊,因为⽤于浏览器,需要下载⽂件,如果也采⽤同步导⼊会对渲染有很⼤影响。
-
CommonJS 在导出时都是值拷⻉,就算导出的值变了,导⼊的值也不会改变,所以如果想 更新值,必须重新导⼊⼀次。
但是 ES Module 采⽤实时绑定的⽅式,导⼊导出的值都指向同⼀个内存地址,所以导⼊值会跟随导出值变化。 -
ES Module 会编译成 require/exports 来执⾏的。
// 引⼊模块 API import XXX from './a.js' import { XXX } from './a.js' // 导出模块 API export function a() {} export default function() {}