一、全局变量和局部变量

  1. // 全局变量:可以在任意位置访问的量就叫全局变量
  2. // 全局变量从创建的那一刻起就会一直保存在内存中
  3. var age = 20;
  4. function a(){
  5. console.log(age);
  6. }
  7. a(); //20
  8. // 局部变量:比如函数中用var定义的变量,只能在函数中访问这个变量,函数外部访问不了
  9. // 局部变量当函数运行完以后就会销毁这个变量
  10. function b(){
  11. var age = 20;
  12. }
  13. console.log(age); // Uncaught ReferenceError: age is not defined

所有的自由变量的查找,是在函数定义的地方,向上级作用域查找

二、什么是闭包?

2-1 闭包原理

闭包是基于正常的垃圾回收处理机制下的,也就是说,一般情况一个函数(函数作用域)执行完毕,里面声明的变量会全部释放,被垃圾回收器回收。但闭包利用一个技巧,让作用域里面的变量,在函数执行完之后依旧保存没有被垃圾回收处理掉。

2-2 闭包优缺点

优点:方便调用上下文中声明的局部变量,逻辑紧密,可以在一个函数中再创建个函数,避免了传参的问题
缺点:因为使用闭包,可以使函数和变量在执行完后不被销毁,保留在内存中,如果大量使用闭包就会造成内存泄露,内存消耗很大

三、常见的闭包写法

3-1 闭包的形成

在嵌套函数中,父函数调用后返回一个子函数,并且返回的这个子函数使用了父函数作用域定义的局部变量,导致该变量在父函数执行完毕后并没有被垃圾回收机制回收,从而形成了一个闭包

  1. function bar(){
  2. var num = 0;
  3. function foo(){
  4. num ++;
  5. console.log(num);
  6. }
  7. return foo;
  8. }
  9. var f = bar();
  10. f(); //1
  11. f(); //2

代码解析:在bar函数中,声明一个变量num,他属于bar函数作用域下的变量。bar函数返回一个foo函数,这个函数被嵌套,函数内部弹出num。首先执行var f = bar();那么bar就执行了,但是执行完毕之后,num变量并没有被释放回收,因为bar函数返回值里面还等待使用这个变量,这个时候foo()就是包含bar()内部作用域的闭包,使得该作用域能够一直存活,不会被垃圾回收机制处理掉,这就是闭包的作用,以供foo()在任何时间进行引用。

  1. var a = (function(){
  2. var num = 0;
  3. function b(){
  4. num ++;
  5. console.log(num);
  6. }
  7. return b;
  8. })();
  9. a(); //1
  10. a(); //2

四、闭包经典场景

  1. var btnList = document.getElementsByClassName("btn")
  2. forvar i = 0; i < 6; i++){
  3. btnList[i].onclick = function(){
  4. console.log("第"+i+"个按钮被点击到了")
  5. }
  6. }
  7. //无论点哪个btn,打印的都是“第6个按钮被点击到了”
  1. var btnList = document.getElementsByClassName("btn")
  2. forvar i = 0; i < 6; i++){
  3. (function(j){
  4. btnList[j].onclick = function(){
  5. console.log("第"+j+"个按钮被点击到了")
  6. }
  7. })(i)
  8. }
  9. //for循环每一次都执行一个自执行函数,每一次变量 i 被当做参数传到自执行函数中去,那么这个自执行函数相当于每次创建了一个变量就是参数 j ,然后第j个元素节点 btnList 绑定一个onclick事件,执行函数里面需要用到这个参数 j ,但是你又没点 , 所以 j 就没有被清理 , 就一直在参数里面被保存着 , 每一个自执行函数都做一样的事情 ,所以这个时候就产生了闭包 ,变量 j 并没有被回收,依然在等待你使用。