什么是闭包
从作用上说:既能重用一个变量,又能保护变量不会被全局污染的一种编程方式
闭包的本质/原理
外层函数的作用域对象,被内层函数引用着无法释放,就形成闭包对象
要理解上面这两句话首先我们来看一个小例子
<script>
var a = 100;
function fun() {
a = 200;
a++;
console.log(a);
}
fun();
console.log(a);
</script>
这个例子输出的结果是什么呢?以上面的例子来分析代码执行的过程
- 当浏览器开始扫描时,这时候还没有执行代码。只是先声明变量,将变量先存储在window对象中(可打印一下console.log(window)看一看里面的变量)
代码执行后,函数会自动创建临时函数作用域对象
当代码执行完成后,会把临时创建的函数作用域对象给清空
所以函数中的a是全局的变量a,所以两次打印出来的值都是201。那如果把上面的例子改成下面的代码会怎么样呢?
var a = 100;
function fun() {
var a = 200;
a++;
console.log(a);
}
fun();
console.log(a);
这个例子输出的结果是两次201吗?我们来输出看一看
这为什么两次的输出不同呢?因为函数里面声明了一个a变量,在函数里面声明的变量会存在临时创建的函数作用域对象中,函数内部的变量a会先去临时创建的函数作用域对象中查找,找到有a变量就拿函数作用域对象的变量来赋值,就不会去到window对象中找变量a,所以window对象中的a变量不受影响。
到代码执行结束后临时创建的函数作用域对象就会被回收,所以函数中的答打印是201,函数外的a变量没有受到影响所以还是100
所以得出结论
1.使用全局变量:可以被重复使用,但是极易被污染和篡改。
2.使用局部变量:只能在函数内使用,出了函数用不了,绝对不会被篡改,但是不能重用。
那我想我的变量不被全局给污染和篡改又能重用应该怎么办?
这时候就要用到最上面闭包了,使用闭包就能实现这个需求,那闭包如何使用呢?看下面的例子
<script>
//需求: 定义一个函数为小孩儿管理压岁钱
// 小孩儿每花一笔钱,可以从总钱数中扣除花的钱,提示还剩xxx
//1. 定义一个外层函数,包裹要保护的变量和内层函数
function parent() {
var total = 1000;
//2. 外层函数将内层函数对象返回到函数外部,让外部可用
//不用给内层函数起名字
return function (money) {
//小孩儿 //局部
total -= money;
console.log(`花了${money}, 还剩${total}`);
};
}
//3. 想用内层函数的人需要调用外层函数,才能获得返回出来的内层函数,保存在变量中,反复使用。
var pay = parent();
total = 0; //试图篡改total变量 结果total不能被篡改
pay(100); //花了100, 还剩900
pay(100); //花了100, 还剩800
pay(100); //花了100, 还剩700
var pay1 = parent();
pay1(100); //重新形成一个闭包 花了100, 还剩900
</script>
看看输出结果
闭包的形成如下图
在parent函数正在被调用的那瞬间,就会创建一个临时函数作用域对象
然后调parent后,由于pay一直引用这parent创建的临时函数作用域,导致这个临时创建出来的函数作用域无法被释放,而形成了闭包
总结成一句话
外层函数的作用域对象,被内层函数引用着无法释放,就形成闭包对象
闭包的缺点
比一般的函数多占用一块内存空间——多占外层函数的作用域对象
如何解决这个缺点呢?以上面的例子来说,如果pay不用到了,就将把pay = null,释放掉函数作用对象。这样就不会多占外层函数的作用域对象
这是我个人对闭包的理解,如果说得不好请多多指教。