• JS闭包


      什么是闭包:

      函数和对其周围状态(词法环境--词法作用域)的引用捆绑在一起构成闭包;这个环境包含了闭包创建时所能访问到所有局部变量,这个概念非常重要!

      闭包的环境是独立的、互不干扰的;结合上面的概念,可以得知,指的是所能访问到的所有的局部变量;

      闭包是函数和其所在的词法作用域的组合;词法作用域包含了它所能访问到的所有局部变量;

      在JavaScript中,每当函数被创建时,就会在函数生成时生成闭包;


     

      闭包的作用:

    1. 主要是使局部变量常驻内存;普通函数的局部变量在函数执行完毕后,会被垃圾回收机制回收!
    2. 使从函数外部访问函数内的局部变量成为可能

      闭包的缺点:

    1. 会造成内存泄漏,有一块内存被占用,而不被释放;
    2. 闭包会在父函数外部,改变父函数内部变量的值;所以,如果把父函数当作对象使用,把内部变量当作私有属性,把闭包当作公有方法,可以在父函数外部通过访问公有方法改变私有变量的值;一定要注意,不要随意改变内部变量的值;(联想Java中,私有属性一般只是提供访问,而不能更改的;所以JavaScript中,用这种方法模拟对象,也一定不要改变私有属性的值!)

     

      词法作用域,若闭包引用的是全局变量,那么这个变量被同类闭包(由同一个函数创建的闭包)共享,若闭包引用的变量是局部变量,那么闭包对于这个变量的引用是独立的、互不影响的;

      各个闭包所处环境是独立的、互不干扰的;每当函数被调用时,若函数的引用地址不同,都会重新创建一个新的地址,也就是重新生成了一个闭包;

      在JavaScript中,每当函数被创建时,就会在函数生成时生成闭包;

      理论知识需要实践来验证!


      从最简单的函数开始

    function fn(){
      var num = 0;
      console.log(++num); 
    }//函数被创建时,在函数生成时生成了闭包;
    fn();//创建了一个闭包并且调用了函数,引用的num是局部变量,属于闭包环境中的变量,而各个闭包的环境是独立的、互不影响的;
    fn();//创建了一个闭包并且调用了函数,但是两个闭包引用的变量num是处于局部作用域下的,所以是独立的、互不影响;输出如下:1,1;
    var num = 0;
    function fn(){
      console.log(++num);
    }
    fn();//创建了一个闭包并调用
    fn();//创建了一个闭包并调用,但是两个闭包引用的变量num是处于全局作用域下的
    //(也就是这个变量并不是处于闭包环境下的,闭包通过作用域链从全局作用域访问到),所以变量共享,输出如下:1,2;

      嵌套函数中的闭包

    function father(){
      var num = 0;
      function son(){
        console.log(++num);
      }
      return son;
    }
    var fn1 = father();//创建了一个闭包,fn1指向的是内部函数son,
    var fn2 = father();//创建了另一个闭包,fn2指向的是内部函数son
    fn1();//引用的变量num是处于闭包环境下的,所以互不干扰,(闭包环境包括了其能访问到的所有局部变量,也就是包含了变量num 


    fn2();//输出如下:1,1
    fn1();//输出2
    var num = 0;
    function father(){
      function son(){
        console.log(++num);
      }
      return son;
    }
    var fn1 = father();//创建了一个闭包(生了一个函数),fn1指向的是内部函数son
    var fn2 = father();//创建了一个闭包,fn2指向的是内部函数son
    fn1();//引用的变量num是处于全局作用域下的,所以变量共享
    fn2();//输出如下:1,2;

      个人总结:闭包就是函数,其对于变量的访问,与普通函数对变量的访问规则是一样的,首先都是通过作用域链查找变量,如果变量是处于词法作用域中的全局作用域下,所以该变量会被所有闭包共享;

      各个闭包的环境是独立的、互不影响的;这句话什么意思呢?环境指的是函数所处的词法环境,包含了所有能访问到的局部变量,不包含全局变量哦,配合这个理解,就很好明白所有函数都是闭包这个概念了,

      掌握执行函数时,便创建了一个闭包并调用的概念,也就是说两次执行函数,生成了两个闭包,它们对于函数内部变量的作用是互不干扰的,也就是说闭包的所处环境是独立的,互不干扰的(理解为对于局部变量的引用是互不干扰的);可以从上面第一和第三个例子验证

     

      上文重复强调了函数的词法作用域,也就是闭包环境,仅仅是包括了函数所能访问到的所有局部变量!!

      每个闭包环境都是独立互不干扰的;

      闭包中可以通过作用域链访问到闭包环境外的变量---也就是全局作用域下的变量,该变量是共享的;


      闭包的应用:

    1. 用闭包模拟私有方法;在Java中,是支持将方法声明为私有的,私有方法只能被同一类中的其他方法访问;
      var counter = function(){
        var privaterNum = 18;
        function changeBy(val){
          privateNum += val;
        }
        return {
        increment: function(){
          changeBy(1);
        },
        decrement: function(){
          changeBy(-1);
        },
      value: function(){
      return privateNum;
      } } }();//立即执行函数中,返回了一个闭包,闭包内的词法环境被三个函数共享 counter.increment();
      //privateNum == 19
      console.log(counter.value()
      );//19

      2、在循环中创建闭包

      for(var i = 0; i < 4; i++){
        setTimeout(function(){
          console.log(i);
        },1000)
      }
      //直接输出四个4,在闭包环境中找不到i变量,沿着作用域链访问到全局变量i,在定时器回调输出时,i已经变成了4
      for(var i = 0; i < 4; i++){
        (function(i){
          setTimeout(function(){
            console.log(i);
          },1000*i)
        })(i);
      }
      //输出0,1,2,3每隔一秒输出一个;定时器回调输出i,在闭包环境中找到了各自的局部变量i,(局部变量是不会受到其他作用域的影响的);

       3、函数工厂

      function makeSizer(size){
        return function(){
          document.body.style.fontSize = size + 'px
        }
      }
      var size12 = makeSizer(12);
      var size14 = makeSizer(14);//函数工厂生成函数,

       

  • 相关阅读:
    前端备战21秋招之操作系统,线程/进程/死锁
    前端备战秋招之计算机网络,这一篇足矣
    VS Code项目中共享自定义的代码片段方案
    eslint插件开发教程
    2020前端春招经验分享,从面试小白到老油条的蜕变
    使用nodejs从控制台读入内容
    js实现展开多级数组
    js使用typeof与instanceof相结合编写一个判断常见变量类型的函数
    07-数据结构
    06-流程控制
  • 原文地址:https://www.cnblogs.com/joeynkay/p/12764923.html
Copyright © 2020-2023  润新知