简单前置
一个函数在执行完成之后其内部的作用域与作用域保存的变量会被销毁,也就是在内存空间中删除
例子:
function $A(){
var name = '悟空';
function $B(){
console.log(name,'==> 函数$B打印');
}
$B();
}
$A();
// 结果 悟空 ==> 函数$B打印
上述作用域关系由词法作用域工作模式可以得出,是$A函数作用域包裹$B函数作用域,在寻找name变量时$B函数会优先在自身作用域中寻找该变量未找到变量name,而后去外层作用域也就是$A函数作用域中寻找,找到了变量name,得到最终打印结果
提出问题
我们知道函数在执行完毕之后其作用域与其中保存的变量就会被销毁,但是我们不想要$A函数作用域被销毁,而是希望$B函数可以在其他的地方继续使用$A函数作用域该怎么办?
闭包
这时就可以使用闭包,闭包可以保证$A函数作用域不被销毁
简单理解: 一个函数可以在其他位置访问该函数的外层作用域,就是闭包
例子:
var name = '八戒';
function $A(){
var name = '悟空';
function $B(){
console.log(name,'==> 函数$B打印');
}
return $B;
}
var res = $A();
res();
// 结果 悟空 ==> 函数$B打印
// 而不是 八戒 ==> 函数$B打印
当$A函数执行后,会将$B函数作为执行结果返回,此时不仅仅返回的是$B函数同时一并将$A函数作用域也返回了出去,这时调用res函数就会打印以上以上结果
这就是闭包想做的事情,让一个本该销毁的函数作用域继续留存下来,等待下一次在其他位置使用
当然,闭包也可以不用返回请看以下情况
情况一:
var name = '唐僧';
function $A(){
var name = '悟空';
function $B(){
console.log(name,'==> 函数$B打印');
}
function $C(fn){
var name = '沙师弟';
fn();
}
$C($B);
}
$A();
// 结果 悟空 ==> 函数$B打印
此种情况是不是很像回调函数,没错回调函数就是使用了闭包
情况二:
var fn = null;
function $A(){
var name = '悟空';
function $B(){
console.log(name,'==> 函数$B打印');
}
fn = $B;
}
$A();
fn();
// 结果 悟空 ==> 函数$B打印
可以用更粗暴的方式理解,只要不是在原来位置实用一个函数的外层作用域就是闭包
模块
模块就是基于闭包原理
例如:
function modeSpace(){
var name = '孙悟空',
gender = 'male';
function getName(){
console.log(name);
}
function getGender(){
console.log(gender);
}
return {
getName,
getGender
}
}
上面的代码就是模块的基本形式,当函数modeSpcae执行时会将函数getName,getGender返回出去,这两个函数会一直持有函数modeSpcae的作用域并有权访问其内部存储的变量,也就是形成了闭包
执行函数后使用变量接收返回的对象,通过对象.属性的方式即可使用模块内部的方法,或者在打打开模块内部的情况下对模块内部进行修改
function modeSpace(){
var name = '孙悟空',
gender = 'male';
function getName(){
console.log(name);
}
function getGender(){
console.log(gender);
}
return {
getName,
getGender
}
}
let res = modeSpace();
res.getName();
// 调用模块内部的方法 结果 孙悟空
res.changeName('猪八戒');
res.getName();
// 修改模块内部的属性 结果 猪八戒
模块拥有很多优点,我总结我想到的几个优点
第一点: 可以隔绝变量,防止变量污染全局环境,也避免了多人开发可能会出现的变量名称冲突问题
第二点: 复用性强,保存一个通用模块,在需要使用到的地方直接引入使用即可
第三点: 使用时可以按需加载,需要使用时在进行下载,不需要就不加载,提升了JS程序的性能
???
闭包似乎是使用词法作用域工作模型后衍生出来的一种技术手段
注
该文章仅仅是学习过程的中理解,如果存在问题,欢迎提出与讨论
参考书籍
《YOU DON'T KNOW JavaScript》