「这是我参与2022首次更文挑战的第15天,活动详情查看:2022首次更文挑战」。
在介绍各种模块化规范之前,我们先来了解一下没有模块化规范的时候我们是怎么解决模块化的。
早期没有模块化的时候,会存在命名冲突的问题,原因在于多个js文件之间没有自己的作用域。
我们现在来模拟一下没有模块化的情况。假设现在有Mike和Jerry有两个人开发,他们都在自己的文件夹下写代码。
Mike在Mike文件夹下的index.js写了如下代码。
var name = "Mike"
var isFlag = true
然后Mike在同一个文件夹下的Mike.js写代码,使用了index.js中的name和isflag变量。
if(isFlag) {
console.log('我的名字:', name);
}
然后在html中引入这两个文件并运行,可以在浏览器中看到打印。
<body>
<script src="./Mike/index.js"></script>
<script src="./Mike/Mike.js"></script>
</body>
而另一个人Jerry也在同时开发,他在自己的Jerry文件夹下的index.js文件中写代码。他同Mike一样,定义了isFlag变量,但是赋值为false。
var name = 'Mike'
var isFlag = false
同样地,他在html文件中也引入了自己的文件。
<body>
<script src="./Mike/index.js"></script>
<script src="./Jerry/index.js"></script>
<script src="./Mike/Mike.js"></script>
</body>
此时,Mike运行了代码,控制台什么都不输出。此时他去找bug是十分困难的,因为他在自己的文件夹下的代码没有问题,他必须去其他人的文件夹找。
我们可以看到早期没有模块化的时候,这样的做法是很容易产生命名冲突的,因为各个js文件是没有自己的作用域的。
那么早期为了解决这个问题是把代码写在函数里面,因为早期是只有函数是有作用域的。
function foo() {
var name = "Mike"
var isFlag = true
}
foo()
但是这样写太麻烦了,还要进行调用。所以我们使用自执行函数,既有自己的作用域不会相互影响,又可以直接执行代码。
(function() {
var name = "Mike"
var isFlag = true
})()
这样做给各个js文件增加了作用域,解决了命名冲突问题,但是又出现了一个问题,那就是在全局不能使用各个js文件定义的变量。
有个办法是将外界需要的用到的变量通过对象的形式return出去,然后用一个新的变量接收这个对象。
var moduleA = (function() {
var name = "Mike"
var isFlag = true
return {
name: name,
isFlag: isFlag
}
})()
在另一个Mike.js文件中使用name,isFlag变量的时候就需要使用新变量来获取。
(function() {
if(moduleA.isFlag) {
console.log('我的名字:', moduleA.name);
}
})()
这样我们就可以得到结果了。
但是,这样做还是有问题。第一个问题就是返回的对象需要用一个变量接收,又容易造成命名冲突,所以有些公司为了解决这个问题,使用moduleA,moduleB,moduleC...来命名。第二个问题,在其他js文件中使用导出的变量,需要清楚知道这个导出的变量名。第三个问题是各个公司会有不同的规范,到了另一家公司需要遵守这家公司的规范,没有统一的规范。
所以后来,社区出现了很多关于模块化的规范,包括AMD,CMD,commonJS等等,以及到了ES6,javascript推出了自己的模块化方案。关于这些规范,下篇文章继续介绍。