闭包在红宝书中的解释就是:有权访问另一个函数作用域中的变量的函数。

1.变量作用域

全局变量:所有的函数外部定义的变量,它的作用域是整个script。

局部变量:定义在函数体内部的变量,作用域仅限于函数体内部。离开函数体就会无效。再调用就是出错。

举例如下-局部变量:

  1. <script type="text/javascript">
  2. function fun(){
  3. var a = 100;
  4. }
  5. console.log(a);
  6. </script>

a变量定义在fun函数内,是局部变量,所以它不能在外部被访问。

举例如下-全局变量:

  1. <script type="text/javascript">
  2. var c = 100;
  3. function fun(){
  4. var a = 100;
  5. console.log(c)
  6. }
  7. fun();
  8. console.log(c);
  9. </script>

在全局定义一个全局变量c,不仅能在fun函数内部被访问,在函数外依旧能被访问。

2.间接访问局部变量

  1. <script type="text/javascript">
  2. function fun(){
  3. var a = 100;
  4. function fun1(){
  5. console.log(a);
  6. }
  7. fun1();
  8. }
  9. fun();
  10. </script>

通过调用fun1把a打印出来,fun1是可以访问fun的所有变量

3.作用域链

可以参考我的这篇文章JS之预编译和执行顺序(全局和函数)可以更好的理解预编译的原理,为作用域链做准备。

举例:

  1. <script type="text/javascript">
  2. var a = 100;
  3. function fun(){
  4. var b = 200
  5. function fun2(){
  6. var c = 300
  7. }
  8. function fun3(){
  9. var d = 400
  10. }
  11. fun2()
  12. fun3()
  13. }
  14. fun()
  15. </script>

首先预编译,一开始生成一个GO{

  a:underfined

  fun:function fun(){//fun的函数体

      var b = 200
      function fun2(){
        var c = 300
      }
      function fun3(){
      var d = 400
      }
      fun2()
      fun3()
    }

}

逐行执行代码,GO{

  a:100

  fun:function fun(){//fun的函数体

      var b = 200
      function fun2(){
        var c = 300
      }
      function fun3(){
      var d = 400
      }
      fun2()
      fun3()
    }

}

当fun函数执行时,首先预编译会产生一个AO{

  b:underfined

  fun2:function fun2(){
       var c = 300
     }

  fun3:function fun3(){
      var d = 400
     }

}

这里注意的是fun函数是在全局的环境下产生的,所以自己身上挂载这一个GO,由于作用域链是栈式结构,先产生的先进去,最后出来,

在这个例子的情况下,AO是后于GO产生的,所以对于fun函数本身来说,执行代码的时候,会先去自己本身的AO里找找看,如果没有找到要用的东西,就去父级查找,此题的父级是GO

此刻fun的作用域链是  第0位    fun的AO{}

          第1位    GO{}

fun函数开始逐行执行AO{

  b:200

  fun2:function fun2(){
       var c = 300
     }

  fun3:function fun3(){
      var d = 400
     }

}

注意:函数每次调用才会产生AO,每次产生的AO还都是不一样的

然后遇到fun2函数的执行,预编译产生自己的AO{

  c:underfined

}

此刻fun2的作用域链是第0位    fun2的AO{}

          第1位    fun的AO{}

          第2位    GO{}

然后遇到fun3函数的执行,预编译产生自己的AO{

  d:underfined

}

此刻fun3的作用域链是第0位    fun3的AO{}

          第1位    fun的AO{}

          第2位    GO{}

fun2和fun3的作用域链没有什么联系

当函数fun2和fun3执行完毕,自己将砍掉自己和自己的AO的联系,

最后就是fun函数执行完毕,它也是砍掉自己和自己AO的联系。

这就是一个我们平时看到不是闭包的函数。

4.闭包

1.闭包在红宝书中的解释就是:有权访问另一个函数作用域中的变量的函数。

2.写法:

  1. <script type="text/javascript">
  2. function fun1(){
  3. var a = 100;
  4. function fun2(){
  5. a++;
  6. console.log(a);
  7. }
  8. return fun2;
  9. }
  10.  
  11. var fun = fun1();
  12. fun()
  13. fun()
  14. </script>

3.效果如下:

4.分析:

执行代码

GO{

fun:underfined

fun1:function fun1()

   {

     var a = 100;

     function fun2()

    {

        a++;

        console.log(a);

     }

     return fun2;

     }

}

然后第十一行开始这里,就是fun1函数执行,然后把fun1的return赋值给fun,这里比较复杂,我们分开来看,

这里fun1函数执行,产生AO{

a:100

fun2:function fun2(){

    a++;
    console.log(a);
    }

}

此刻fun1的作用域链为 第0位   AO

           第1位   GO

此刻fun2的作用域链为 第0位   fun1的AO

           第1位   GO

解释一下,fun2只是声明了,并没有产生调用,所以没有产生自己的AO,

正常的,我们到第7行代码我们就结束了,但是这个时候来了一个return fun2,把fun2这个函数体抛给了全局变量fun,好了,fun1函数执行完毕,消除自己的AO,

此刻fun2的作用域链为 第0位   fun1的AO

           第1位   GO

第十二行就是fun执行,然后,它本身是没有a的,但是它可以用fun1的AO,然后加,然后打印,

因为fun中的fun1的AO本来是应该在fun1销毁时,去掉,但是被抛给fun,所以现在fun1的AO没办法销毁,所以现在a变量相当于一个只能被fun访问的全局变量。

所以第十三行再调用一次fun函数,a被打印的值为102.

5.闭包之深入理解

举例1:

  1. <script type="text/javascript">
  2. function fun1(){
  3. var a = 100;
  4. function fun2(){
  5. a ++;
  6. console.log(a);
  7. }
  8.  
  9. return fun2;
  10. }
  11. var fn1 = fun1(); //生成自己的AO,上面有a
  12. var fn2 = fun1();
  13. fn1()//
  14. fn1()//
  15. fn2()//
  16. </script>

fn1和fn2互不干涉,因为fun1函数调用了两次,所以两次的AO是不一样的。

举例2:

  1. <script type="text/javascript">
  2. function fun(){
  3. var num = 0;
  4. function jia(){
  5. num++;
  6. console.log(num);
  7. }
  8. function jian(){
  9. num--;
  10. console.log(num)
  11. }
  12. return [jia,jian];
  13. }
  14. var fn = fun();
  15. var jia = fn[0];
  16. var jian = fn[1];
  17. jia()//
  18. jian()//
  19. </script>

jia和jian共用一个fun的AO,一动全都动,十二行返回了一个数组,

举例3:

  1. <script type="text/javascript">
  2. function fun(){
  3. var num = 0;
  4. function jia(){
  5. num++;
  6. console.log(num);
  7. }
  8. function jian(){
  9. num--;
  10. console.log(num)
  11. }
  12. return [jia,jian];
  13. }
  14. var jia = fun()[0];
  15. var jian = fun()[1];
  16. jia()//
  17. jian()//-1
  18. </script>

这里有一个坑,jia = fun()[0]; jian = fun()[1];fun函数执行了两遍,所以两次的AO不一样,所以jia和jian操作的对象不一样。

6.闭包好处与坏处

好处:

①保护函数内的变量安全 ,实现封装,防止变量流入其他环境发生命名冲突

②在内存中维持一个变量,可以做缓存(但使用多了同时也是一项缺点,消耗内存)

③匿名自执行函数可以减少内存消耗

坏处:

①其中一点上面已经有体现了,就是被引用的私有变量不能被销毁,增大了内存消耗,造成内存泄漏,解决方法是可以在使用完变量后手动为它赋值为null;

②其次由于闭包涉及跨域访问,所以会导致性能损失,我们可以通过把跨作用域变量存储在局部变量中,然后直接访问局部变量,来减轻对执行速度的影响

7.闭包解决的问题

  1. <!DOCTYPE html>
  2. <html>
  3. <head>
  4. <meta charset="UTF-8">
  5. <title></title>
  6. </head>
  7. <body>
  8. <ul>
  9. <li>0</li>
  10. <li>1</li>
  11. <li>2</li>
  12. <li>3</li>
  13. <li>4</li>
  14. <li>5</li>
  15. <li>6</li>
  16. <li>7</li>
  17. <li>8</li>
  18. <li>9</li>
  19. </ul>
  20. <script type="text/javascript">
  21. var lis = document.getElementsByTagName("li");
  22. for(var i = 0;i < lis.length;i++){
  23. lis[i].onclick = function(){
  24. console.log(i)
  25. }
  26.  
  27. }
  28. </script>
  29. </body>
  30. </html>

不管点击哪个都是10,那是因为点击事件是我们点击才触发的函数,等到触发的时候,i早就变成10跳出循环了,,这个时候我们就需要立即执行函数,创造了十个不同的作用域

解决方案:

  1. <!DOCTYPE html>
  2. <html>
  3. <head>
  4. <meta charset="UTF-8">
  5. <title></title>
  6. </head>
  7. <body>
  8. <ul>
  9. <li>0</li>
  10. <li>1</li>
  11. <li>2</li>
  12. <li>3</li>
  13. <li>4</li>
  14. <li>5</li>
  15. <li>6</li>
  16. <li>7</li>
  17. <li>8</li>
  18. <li>9</li>
  19. </ul>
  20. <script type="text/javascript">
  21. var lis = document.getElementsByTagName("li");
  22. for(var i = 0;i < lis.length;i++){
  23. // lis[i].onclick = function(){
  24. // console.log(i)
  25. // }
  26. (function(i){
  27. lis[i].onclick = function(){
  28. console.log(i)
  29. }
  30. })(i)
  31. }
  32. </script>
  33. </body>
  34. </html>

JS之闭包详细解读的更多相关文章

  1. JS中的闭包 详细解析大全(面试避必考题)

    JS中闭包的介绍   闭包的概念 闭包就是能够读取其他函数内部变量的函数. 一.变量的作用域 要理解闭包,首先必须理解Javascript特殊的变量作用域. 变量的作用域无非就是两种:全局变量和局部变 ...

  2. JS内存空间详细图解

    JS内存空间详细图解 变量对象与堆内存 var a = 20; var b = 'abc'; var c = true; var d = { m: 20 } 因为JavaScript具有自动垃圾回收机 ...

  3. MemCache超详细解读

    MemCache是什么 MemCache是一个自由.源码开放.高性能.分布式的分布式内存对象缓存系统,用于动态Web应用以减轻数据库的负载.它通过在内存中缓存数据和对象来减少读取数据库的次数,从而提高 ...

  4. 关于js中闭包的理解

    1.以前很不理解js中闭包的概念及使用,下面来看一下 function foo() { var a = 123; var b = 456; return function () { return a; ...

  5. js的闭包

    一,关于js闭包的只是感觉很高大上似乎,对于学弱来说任何问题都是这样的,值得去钻研和提高. 资料上理解的都是关于js的闭包其实就是js的变量的作用域的灵活使用. 函数内部定义变量的时候,一定要用 va ...

  6. MemCache超详细解读 图

    http://www.cnblogs.com/xrq730/p/4948707.html   MemCache是什么 MemCache是一个自由.源码开放.高性能.分布式的分布式内存对象缓存系统,用于 ...

  7. 彻底搞清js中闭包(Closure)的概念

    js中闭包这个概念对于初学js的同学来说, 会比较陌生, 有些难以理解, 理解起来非常模糊. 今天就和大家一起来探讨一下这个玩意. 相信大家在看完后, 心中的迷惑会迎然而解. 闭包概念: 闭包就是有权 ...

  8. rpm软件包管理的详细解读

    CentOS系统上使用rpm命令管理程序包:安装.卸载.升级.查询.校验.数据库维护 1.基本安装 rpm -ivh PackageFile 2.rpm选项 rpm -ivh --test Packa ...

  9. MemCache详细解读

    MemCache是什么 MemCache是一个自由.源码开放.高性能.分布式的分布式内存对象缓存系统,用于动态Web应用以减轻数据库的负载.它通过在内存中缓存数据和对象来减少读取数据库的次数,从而提高 ...

随机推荐

  1. Samurai&#39;s Stroke

    题目链接 题意: 一个长度为L的木棍,有n个支点支撑,每一个点是一个int数.表示距离木棍左端点的距离.求在那些位置将木棍劈开能够使得至少有一个木棍掉下去,输出这些位置的长度 3 ≤ l ≤ 109; ...

  2. LightOJ - 1038 Race to 1 Again 递推+期望

    题目大意:给出一个数,要求你按一定的规则将这个数变成1 规则例如以下,如果该数为D,要求你在[1,D]之间选出D的因子.用D除上这个因子,然后继续按该规则运算.直到该数变成1 问变成1的期望步数是多少 ...

  3. JAVA学习第二十七课(多线程(六))- 多生产者多消费者问题(JDK1.5新特性)

    多生产者多消费者问题 以生产馒头 消费馒头为例. class Resource { private String name; private int count = 1; private boolea ...

  4. 系统丢失的DLL文件问题根源解决(纯净官网下载放心)(图文详解)(博主推荐)

    导言 最近,身边的朋友们,问我,他电脑的win10系统里 mfc110.dll 丢失. 其他的系统文件丢失修复,是一样的步骤. 现象 大家也许,都会有这么一个习惯,动不动则就去百度上搜索. 其实啊,这 ...

  5. 机器学习规则:ML工程最佳实践----rule_of_ml section 3【翻译】

    作者:黄永刚 ML Phase III: 缓慢提升.精细优化.复杂模型 第二阶段就已经接近结束了.首先你的月收益开始减少.你开始要在不同的指标之间做出平衡,你会发现有的涨了而有的却降了.事情变得有趣了 ...

  6. jsp登录页面 雏形

    <%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding= ...

  7. PostgreSQL Replication之第六章 监控您的设置(1)

    在本书的前几章,您已经学习了各种复制以及如何配额制各种类型的场景.现在是时候通过增加监控来让您的设置更加可靠了. 在本章中,您将学习监控什么以及如恶化实施合理的监控车辆.您将学习: • 检查您的 XL ...

  8. JACOB调用控件函数

    背景介绍: 使用JAVA程序,实现对系统已安装控件方法的调用. JACOB下载地址:http://danadler.com/jacob/ 使用方法: 1.将jacob.jar添加到项目工程中 2.将j ...

  9. Mac上vmware虚拟机Windows10安装JDK8及配置环境

    1.jdk8下载地址:http://www.oracle.com/technetwork/java/javase/downloads/index.html 2.双击下载的jdk进行安装 3.安装成功之 ...

  10. MongoDB 的replicattion 复制集练习

              replicattion 相当于 mysql 的主从复制的读写分离,共同维护相同的数据,提高服务器的可用性[假如主(PRIMARY)不能用时,mongo会迅速自动切到从(SECON ...