zoukankan      html  css  js  c++  java
  • 闭包

    闭包

    我们都知道函数由于作用域的存在,外部一般是无法访问函数内部局部变量的:

    1      function f1() {
    2             var a = 1
    3         }
    4         console.log(a);//Uncaught ReferenceError: a is not definedat Untitled-1.html:14

    但是我们可以通过在f1中再定义一个函数f2,根据作用域链的规则,f2是可以访问f1的局部变量的,我们再把f2当作f1的函数返回值,那么f1外部不也可以读取f1的变量了!

     1      function f1() {
     2             var a = 1;
     3             function f2() {
     4                 console.log(a);
     5             }
     6             return f2;
     7         }
     8         
     9         var result = f1()
    10         result()  //1            

     

    这便是一个闭包产生的结果!chrom浏览器会将这个执行上下文的函数名f1称作闭包。

    闭包是一种特殊对象,由某个函数以及它执行上下文共同组成,当该内部函数访问了外部上下文中的值便产生闭包

    形成条件

    • 函数的嵌套
    • 内部函数引用外部函数的标变量

    一个简单闭包应用例子:


    1     for (var i=1; i<=5; i++) {
    2         setTimeout( function timer() {
    3             console.log( i );
    4         }, i*1000 );
    5     }  //我们想要每隔一秒分别输出12345;但实际上每隔一秒产生一个6 共输出5个6

    由于setTimeout运用到js中的异步机制:setTimeout每执行时,会将其内部内容作为异步任务放入异步任务队列中,然后继续执行完所有主线任务,再一步步执行任务队列。

    所以上面实际上内部函数function timer() 一直等待for循环执行完毕后,再开始执行,此时i=6,故异步输出5个6。我们可以用利用闭包来达到想要的效果:

    1     for (var i=1;i<=5;i++){
    2         function f1 () {                       -----------
    3             var j = i;
    4             setTimeout( function timer(){            在此间形成闭包f1     
    5                 console.log(j);
    6             }, j*1000);
    7         }                                      -----------
    8         f1()
    9     }                  此时便会每隔一秒分别输出12345;达到我们本来想要的结果。

       不同点在于第二个做法将循环内部套上了一层函数f1,满足了闭包条件产生了闭包;                                                    通过j保存了每一次循环过后不同的i再通过闭包将其滞留;                                                          尽管异步仍然回等所有主线函数执行完毕最后再一个个执行,但闭包f1的存在导致即便执行异步队列中function timer() 执行时 闭包中f1中的变量j仍然被防止垃圾回收机制销毁.以达到我们想要的效果。

    就好比每次循环都会形成新的不同的作用域,闭包导致每个不同的i都被封闭到不同的作用域中了。


    还可以通过立即执行函数简化成:

    1 for (var i=1; i<=5; i++) {
    2   (function(j){
    3     setTimeout(function timer() {
    4       console.log(j);
    5     }, 0)
    6   })(i)
    7 }

    此处的j=i还只是为了方便理解,可以不写; 

     上述代码等于:

     1   var i = 1;
     2   (function f1(){
     3     var j = i;
     4     setTimeout(function timer() {
     5       console.log(j);
     6     }, j*1000)
     7   })();
     8     var i =2;
     9   (function f1(){
    10     var j = i;
    11     setTimeout(function timer() {
    12       console.log(j);
    13     }, j*1000)
    14   })();
    15     var i =3;
    16   (function f1(){
    17     var j = i;
    18     setTimeout(function timer() {
    19       console.log(j);
    20     }, j*1000)
    21   })();
    22     var i =4;
    23   (function f1(){
    24     var j = i;
    25     setTimeout(function timer() {
    26       console.log(j);
    27     }, j*1000)
    28   })();
    29     var i =5;
    30   (function f1(){
    31     var j = i;
    32     setTimeout(function timer() {
    33       console.log(j);
    34     }, j*1000)
    35   })()

    说白了就是这个例子我们需要每次迭代时创建新的作用域,在这个作用域内形成闭包。

    es6中let声明,除了不形成变量提升,可以形成块级作用域之外,还有更多的不同:可以劫持块级作用域,并在该作用域声明变量。本质上将该块转换成可关闭的作用域。

    如此便可利用let完成上例:

    1   for(var i=1;i<=5;i++){
    2       let j = i;
    3       setTimeout(function timer() {
    4           console.log(j)         
    5       }, j*1000);
    6   }

    根据你不知道的js中指出 for循环头部的let声明还会存在一个特殊行为:指出变量在循环过程中不止被声明一次,即每次迭代都会声明一次,随后每个迭代都会使用上一个迭代结束时的值来初始化该变量。既然如此,那么还有更简单的实现方法:

     

    1  for(let i=1;i<=5;i++){
    2       setTimeout(function timer() {
    3           console.log(i)         
    4       }, i*1000);
    5  }  // 闭包与块级作用域联手天下无敌!

    闭包的一些应用场景:往后深入学习再写

    • 模块化
    • 柯里化
  • 相关阅读:
    安卓手机抓包
    探讨 yum 与 rpm 的安装包数量
    CentOS6.5 yum安装桌面环境
    CentOS6.5使用本地光盘做yum源 (参考:http://www.jb51.net/os/RedHat/43343.html)
    tar、zip 、unzip 打包与压缩 (参考:http://pengyl.blog.51cto.com/5591604/1191197)
    CentOS6.5使用本地光盘做yum源 (参考:http://www.jb51.net/os/RedHat/43343.html)
    mount 挂载光盘
    Shell编程
    Vim实现批量注释的方法
    Linux基本命令
  • 原文地址:https://www.cnblogs.com/zxf906/p/15105509.html
Copyright © 2011-2022 走看看