刨根问底看闭包

在学习TypeScript时,回想起原来所学的闭包,一知半解,本篇文章就系统的了解一下闭包。

第一部分: 我的世界你不懂-----js的作用域

上代码:

if(true){
       var a = 1;
   }
   console.log(a);

运行结果:
在这里插入图片描述
分析:
卧槽???为什么他一个花括号里面定义的变量我在花括号外边还可以使用??? 。JavaScript的世界你不懂,他就是要当一只特立独行的猪。在JS中for循环,if等分支语句的花括号都不算是我们原来所以为的块级作用域!!!
你觉得我这句话说得没有权威性吗?我就请一个大哥TypeScript来撑一下场子!

 变量是定义在if语句里面,但是我们却可以在语句的外面访问它。 这是因为 var声明可以在包含它的函数,模块,命名空间或全局作用域内部任何位置被访问,包含它的代码块对此没有什么影响。

我们可以讲这句话中目前所用到的部分摘出来单独看一下

 var声明可以在包含它的函数内部任何位置被访问

因此我们才得出来在js中由于var声明的原因造成了这种奇葩的现象,因此在ES6之后的包括TypeScript都建议使用let和const来定义变量。

第二部分:js世界的规则----垃圾回收机制

垃圾回收机制是每一个高级语言所具有的。
照旧,我们先看一段代码

 function showNum(){
      var num = 1;
      num++;
      console.log(num)
  }
  showNum();
  showNum();
  showNum();

运行结果:
在这里插入图片描述
从运行结果上来看是没有什么不对,很完美。-_-
再看一段代码:

function showNum2(){
      var num = 1;
     return function(){
          num++;
          console.log(num);
      }
  }
  var add = showNum2();
    add();
    add();
    add();

运行结果:
在这里插入图片描述
运行出现2,3,4而其前面的例子就只出现2,2,2,这是为什么???
分析:
在这里就要涉及到js的垃圾回收机制了,js中的垃圾回收是自动进行的,简单介绍一下最常用的两种垃圾回收机制:【请耐心看完】
1:标记清除法:两个阶段,标记和清除。第一阶段将所有用不到的对象都标记一下,第二阶段是将所有标记的对象清除出去。【进入环境的没有标记,离开环境的身上带有标记】。
2:引用计数法:增加一次引用就将引用次数加1,如果引用的位置发生了变化,那么原来位置的引用对象的引用次数就要减去1,一直到0,就可以被清除出去。
在上面的两个例子中,代码1中的函数showNum()在执行完一次之后他内部的那些变量就会被自动回收处理,因此在之后的每一次调用都会将重新的初始化这里面的所有的变量,所以每一次的执行都是重新开始
在代码2中为什么每一次执行add()方法他的变量的值都会进行累加,看起来仿佛js的垃圾回收机制没有起作用,事实上这就是闭包在发挥作用。在通常情况下,执行完函数之后它内部的变量就会被垃圾回收,但是我们使用闭包将它内部的一个子函数返回出来,var add = showNum2();通过return内部的匿名函数的地址来锁止局部变量num,不让他被垃圾回收【我在外边对他有引用,你咋给我回收】,从而实现了局部变量的长久保存。!!!!这就是闭包

好像明白了闭包是什么东西,但是又仿佛有些模糊。

一个拥有许多变量和绑定了这些变量的环境的表达式(通常是一个函数)

这么说吧:闭包就是一个函数,他是嵌套在一个函数内部的子函数,外层函数通过return内层函数从而实现局部变量的外部使用。
明白了么? 小老弟-----

第三部分:闭包下天山-----应用场景

在这一部分,我来总结一下,目前为止我可以使用闭包来做什么
一:延长使用寿命,防止回收
这个在前面第二部分的代码1示例中就有了。
二:闭包可以让外界使用局部变量
这个也在前面第二部分的代码2示例中就有了。
三:解决循环的变量提升问题

    <div>1</div>
    <div>2</div>
    <div>3</div>
 var divArr = document.getElementsByTagName("div");
    for (var i = 0;i < divArr.length;i++){
        divArr[i].onclick = function(){
            console.log(i);
        }
    }

运行结果:
在这里插入图片描述
点击上面三个数字,全部出现3,实际上应该出现0,1,2三个数字,但是为什么会出现这种情况呢????
在js中定时器事件和点击事件等都是异步的
执行的时候是一起执行的,不会说我for循环在第一次赋值i时他会等我第一次进行click,异步现象虽然理解起来比较难,但是结合实际去考虑他还是比较符合实际的,click就是人为的点击进行的啊,for循环这东西能等你去点击的时候才运行吗??很显然是不合理的,他需要异步的支持!,因此当click执行的时候i值已经变成了3。。。因此。。。。。
我们使用闭包来解决这个问题:

var divArr = document.getElementsByTagName("div");
    for (var i = 0;i < divArr.length;i++){
        (function(i){
            divArr[i].onclick = function(){
                console.log(i);
            }
        })(i);
    }

运行效果:
在这里插入图片描述
分析:
匿名函数自调用的形式,使用到了闭包的概念
在这里我们使用一个匿名函数进行定义,同时使用匿名函数自调用的方式同时传入i值,当click事件发生时,他会自己去寻找i值,使用了闭包之后,他会封闭住开始时传过来的i值,因此就不会出现上段代码的问题!!【在es6中使用let声明变量就可以完美的解决,这都是使用var的牛逼后遗症。】

最后的总结:

1:闭包可以用来延长变量的使用寿命,即锁止变量防止被垃圾回收;
2:闭包可以让外界使用局部变量,打破作用域的关隘。
3:解决循环中的变量提升问题【封闭变量】;
4:闭包闭包,封闭的包,进来不要变,老老实实干。

总的来说,层次尚浅,分析问题看待问题的角度仍然是有些稚嫩,如有错误,欢迎指正,非常感谢!!!

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值