写在前面
JS变量作用域有两种:局部变量和全局变量。 JS有其特殊之处:函数内部可以直接读取全局变量(例1),在函数外部无法读取函数内的局部变量(例2)。
//例1
const n=999;
function f1(){
console.log(n);
}
f1(); // 999
//例2
function f1(){
const n=999;
}
console.log(n); // Uncaught ReferenceError: n is not defined
函数内部声明变量的时候,一定要使用let 或 const命令。如果不用的话,你实际上声明了一个全局变量!
如果想得到函数内部的局部变量,应该怎么办?
答案是: 在该函数内部再写一个函数。
//例3
function father(){
const n = 999;
function child(){
console.log(n); //向上寻找到父亲中有变量n
}
return child; //将child作为结果返回,那么在father外就可以读到father内部的变量。
}
const result = father(); //result实际上就是child
result(); // 999
在例3中,father中所有的变量对child都是可见的。反过来child中的变量对father是不可见的。这就是JS语言特有的"链式作用域"结构(chain scope),子对象会一级一级地向上寻找所有父对象的变量。所以,父对象的所有变量,对子对象都是可见的,反之则不成立。
上面的child 就是 闭包。
什么是闭包?
闭包是一个函数,它那能够读取其他函数的局部变量。
对于JS来说,子函数能够读到父函数的局部变量,因此可以把闭包简单理解成"定义在一个函数内部的函数"。
在本质上,闭包就是将函数内部和函数外部连接起来的一座桥梁。
闭包用来做什么?
- 访问函数内部的变量,如例3.
- 让变量保持在内存中,不会在函数调用结束后被垃圾回收机制回收。
//例4
function father(){
let n=999;
nAdd=function(){n+=1} //nAdd全局变量,匿名函数,相当于闭包,可以在函数外部读到函数的局部变量。
function child(){
console.log(n);
}
return child;
}
const result = father();//result实际上就是child,child被赋给全局变量result,导致child一直在内存中,而child依赖father,所以father也一直存在在内存中。
result(); // n = 999
nAdd(); // n = 999+1
result(); // n = 1000
使用闭包需要注意什么?
- 内存消耗。解决办法:在退出函数之前,将不使用的局部变量全部删除。
- 不要随便改变函数内部的值。