JavaScript模块化浅析
什么是模块化
个人通过学习之后的大概认知就是:模块化就是通过将不同功能的代码经过划分,放进不同的文件,通过规范的命名和输出,达到让代码易于管理、维护的一种代码组织方式。
接下来就通过一个小栗子,重现一下学习过程,来验证一下一段代码是怎么逐渐模式化的。
假设我们有源代码如下:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<title>Test</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
</head>
<body>
<script>
var arr = [1,2,3,4,5,6]
for(let i=0;i<arr.length;i++){
console.log(arr[i])
}
function arrReset(arr){
var newArr = arr.filter((value,key)=>{
return value>100
})
console.log(newArr)
}
arrReset([834,54,5,67,6,345,234,6,56,234])
</script>
</body>
</html>
缺点:
1.嵌在HTML页面中,没有做到结构与行为分离,不符合web标准的核心理念。
2.结构繁乱,不利于编写与维护。
根据功能划分
根据代码的功能来划分不同的js文件:
arrLog.js:
//这段代码的作用是打印出一个数组,所以我们单独把他划分出来,命名为arrLog.js
//arrLog.js
var arr = [1,2,3,4,5,6]
for(let i=0;i<arr.length;i++){
console.log(arr[i])
}
arrFilter.js:
//这段代码的作用是根据传入的数组筛选并返回一个符合我们要求的新数组,命名为arrFilter.js
function arrReset(arr){
var newArr = arr.filter((value,key)=>{
return value>100
})
console.log(newArr)
}
arrReset([834,54,5,67,6,345,234,6,56,234])
所以html的js引入标签就变成了这样:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<title>Test</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
</head>
<body>
<script src="./arrLog.js"></script>
<script src="./arrFilter.js"></script>
</body>
</html>
优点:
1.HTML的结构变得简洁。
2.根据js文件的命名可以判断出里面代码的功能,方便维护和编写。
3.提高可复用性,当有其他地方需要用到同一个功能,我们完全可以把这些js文件运用过去。
避免命名空间污染
这样编写js文件方便维护,但是,如果不谨慎处理命名空间,有可能会导致更严重的问题。
什么是命名空间污染
可以看到,无论是arrLog.js
中的window.arr
,还是arrFilter.js
中的arrReset()
,我们都可以在Log在控制台中,这意味着什么?
这意味着,无论是arrLog.js中的某个全局变量,还是arrFilter.js中的某个函数,别的文件都可以访问得到!这就是命名空间污染。
试想一下,如果还存在一个arrOther.js,里面还声明了一个arr数组,或者是一个arrReset函数,那么是不是全都乱套了?
所以,我们应该避免命名空间污染。
如何避免命名空间污染
不使用全局变量,使用局部变量。
在ES6,有了let关键字,可以声明一个局部变量。
但是在ES6之前,一般使用匿名函数来声明局部变量。
- 局部变量尝试 1 - 花括号
使用花括号来避免全局变量:
{
var arr = [1,2,3,4,5,6]
for(let i=0;i<arr.length;i++){
console.log(arr[i])
}
}
结论:不可行,因为会造成变量提升。
实际上,代码会变成:
var arr
{
arr = [1,2,3,4,5,6]
for(let i=0;i<arr.length;i++){
console.log(arr[i])
}
}
- 局部变量尝试 2 - 创建函数
function xxx(){
var arr = [1,2,3,4,5,6]
for(let i=0;i<arr.length;i++){
console.log(arr[i])
}
}
xxx.call()
利用函数来创建一个局部变量,这样外部也访问不到这个变量。
缺点:变量arr是变成了局部变量,但是与此同时,创建了一个全局变量xxx,通过调用他才能获得局部变量,得不偿失。
所以我们要使用立即执行匿名函数,立即执行,没有命名,也就没有所谓的全局变量。
- 局部变量尝试 3 - 匿名函数 1
function(){
var arr = [1,2,3,4,5,6]
for(let i=0;i<arr.length;i++){
console.log(arr[i])
}
}.call()
缺点:Chrome报错,不支持。
- 局部变量尝试 3 - 匿名函数 2
(function(){
var arr = [1,2,3,4,5,6]
for(let i=0;i<arr.length;i++){
console.log(arr[i])
}
}).call()
缺点:一旦前面不小心加上别的消息,则会报错。
如:
xxx
(function(){
var arr = [1,2,3,4,5,6]
for(let i=0;i<arr.length;i++){
console.log(arr[i])
}
}).call()
他就会转变成了:
xxx(function(){
var arr = [1,2,3,4,5,6]
for(let i=0;i<arr.length;i++){
console.log(arr[i])
}
}).call()
他的语义将会完全地被改变。
- 局部变量尝试 3 - 匿名函数取反
直接通过取反的方式来让匿名函数执行。
他同时会影响返回值,但是我们不在乎这个匿名函数的返回值,所以这个方法是可行的。
!function(){
var arr = [1,2,3,4,5,6]
for(let i=0;i<arr.length;i++){
console.log(arr[i])
}
}.call()
优点:利用取反匿名函数的方式,直接执行获得局部变量,而且不会报错。
所以,我们现在的代码就变成了这样:
//arrFilter.js
!function(){
function arrReset(arr){
var newArr = arr.filter((value,key)=>{
return value>100
})
console.log(newArr)
}
arrReset([834,54,5,67,6,345,234,6,56,234])
}.call()
//arrLog.js
!function(){
var arr = [1,2,3,4,5,6]
for(let i=0;i<arr.length;i++){
console.log(arr[i])
}
}.call()
通过立即执行函数的处理,现在两个js文件之间的变量就不能互相影响,造成命名空间污染了。
闭包的使用
但是,如果在某些情况下,arrFilter.js需要操作到arrLog.js文件中的某一个变量,或者是arrLog.js需要用到arrFilter.js的某一个函数,该怎么办呢?
这就需要运用到之前学到的闭包了。
我们给这个变量提供一个外接端口,别的文件可以通过这个端口来访问他:
//arrLog.js
!function(){
var arr = [1,2,3,4,5,6]
for(let i=0;i<arr.length;i++){
console.log(arr[i])
}
window.arrPush = function(num){
arr.push(num)
console.log(arr)
}
}.call()
arrLog.js中有一个数组,我们通过设置一个全局函数,给这个数组添加一个元素,然后把这个数组打印出来。
//arrFilter.js
!function(){
function arrReset(arr){
var newArr = arr.filter((value,key)=>{
return value>100
})
console.log(newArr)
}
arrReset([834,54,5,67,6,345,234,6,56,234])
arrPush(56)
}.call()
arrFilter.js中则调用arrPush函数,添加元素56。
最后得出结果:
我们添加的元素56
已经被添加进了arr里面了。
总结
我们先是使用了模块化使js代码更简洁明了,易于维护;
接着使用立即执行函数对js代码进行封装,创建局部变量,避免命名空间污染;
接着使用了闭包,使得某些情况下A文件可以对B文件中的某一个变量进行间接性操作(但是不能直接访问他)。
关于模块化,这就是一些个人的粗浅的认知,还有更专业更深入的知识,如Commonjs、AMD、CMD、ES6 modules等,以后会慢慢接触到的,循序渐进才是学习的主要方式。