zoukankan      html  css  js  c++  java
  • javaScript基础之闭包的深入理解

    这周要在公司小组内分享一下对闭包的理解。之前自己也是似懂非懂,趁着这次机会也翻看了不少的资料与文章。在这里整理并记录了自己的理解。如果有误欢迎指正~~

    一、变量的作用域

    ECMScript5没有块级作用域。

    变量的作用域无非就是两种:全局变量和局部变量。

    function add(num1, num2) {
      var sum = num1 + num2;
      return sum;
    }
    var result = add(10, 20); //30
    alert(sum); //由于sum是局部变量,外部访问不到。

    使用 var 声明的变量会自动被添加到最接近的环境中。

    在函数内部,最接近的环境就是函数的局部环境;

    如果初始化变量时没有使用 var 声明,该变量会自动被添加到全局环境。 

    例:

    function add(num1, num2) {
      sum = num1 + num2; //全局变量
      return sum;
    }
    var result = add(10, 20); //30
    alert(sum); //30

    总结:就在于函数内部可以直接读取全局变量,在函数外部自然无法读取函数内的局部变量。

    例:

    var color = "blue";
    function getColor(){
      return color;
    }
    alert(getColor()); //"blue"

    对应的作用域链:

    ECMScript6新增let、const、在的代码块内有效。拥有块级作用域。这里不做过的介绍(查看详情请点击此处

    二、闭包的概念

    官方版闭包是指有权访问另一个函数作用域中的变量的函数 ;

    简单易懂(阮一峰老师的解释)闭包就是能够读取其他函数内部变量的函数。由于在Javascript语言中,只有函数内部的子函数才能读取局部变量,因此可以把闭包简单理解成"定义在一个函数内部的函数"。

    所以,在本质上,闭包就是将函数内部和函数外部连接起来的一座桥梁。

    当某个函数被调用时,会创建一个执行环境(execution context)及相应的作用域链。然后,使用 arguments 和其他命名参数的值来初始化函数的活动对象(activation object)。但在作用域链中,外部函数的活动对象始终处于第二位,外部函数的外部函数的活动对象处

    于第三位,……直至作为作用域链终点的全局执行环境。在函数执行过程中,为读取和写入变量的值,就需要在作用域链中查找变量 。(摘录红宝书)

    例:

    function compare(value1, value2){
      if (value1 < value2){
        return -1;
      } else if (value1 > value2){
        return 1;
      } else {
        return 0;
      }
    }
    var result = compare(5, 10);

    三、闭包的作用

    主要的作用:

    1)就是前面提到的可以读取函数内部的变量;

    例:

     function f1(){
    
        var n=999;
    
        function f2(){
          alert(n); 
        }
    
        return f2;
    
      }
    
      var result=f1();
    
      result(); // 999

    2)就是让这些变量的值始终保持在内存中;

    例:

    function f1(){
    
        var n=999;
    
        nAdd=function(){n+=1}
    
        function f2(){
          alert(n);
        }
    
        return f2;
    
      }
    
      var result=f1();
    
      result(); // 999
    
      nAdd();
    
      result(); // 1000
    无论什么时候在函数中访问一个变量时,就会从作用域链中搜索具有相应名字的变量。一般来讲,当函数执行完毕后,局部活动对象就会被销毁,内存中仅保存全局作用域(全局执行环境的变量对象)。然而闭包的情况有点不同。

    f1函数内部定义的函数f2会将包含函数f1的活动对象添加到它的作用域链中。f2函数的作用域链被初始化为包含f1
    函数的活动对象和全局变量对象 ,在f1函数执行完毕后,活动对象也不会销毁,因为f2的作用域仍然引用f1的活动对象。换句话说就是,f1的作用域链会被销毁,但是活动对象还存在内存中,直到f2函数被销毁。

    即:
    result = null;

    四、注意事项

    4.1.闭包与变量
    闭包只能取得包含函数中任何变量的最后一个值;

    例:
    function createFunctions(){
      var result = new Array();
      for (var i=0; i < 10; i++){
        result[i] = function(){
        return i;
      };
    }
      return result;
    }

    这个函数会返回一个函数数组。表面上看,似乎每个函数都应该返自己的索引值,即位置 0 的函数返回 0,位置 的函数返回 1,以此类推。但实际上,每个函数都返回 10

    因为每个函数的作用域链中都 保 存 着 createFunctions() 函 数 的 活 动 对 象 , 所 以 它 们 引 用 的 都 是 同 一 个 变 量 每个函数都引用着保存变量 i 的同一个变量对象,所以在每个函数内部 的值都是 10 。

    用我的话理解就是,i相对于匿名函数(闭包)来说是全局变量,全局只有一个变量i。每一次循环,变量i的值都会发生改变,而循环内被赋给数组result的函数内部的i指向的就是全局的i。也就是说,所有数组a的成员里面的i,指向的都是同一个i

    导致运行时输出的是最后一轮的i的值,也就是 10。

    解决办法:

    function createFunctions(){
      var result = new Array();
      for (var i=0; i < 10; i++){
        result[i] = function(num){
        return function(){
          return num;
          };
        }(i);
      }
      return result;
    }

    4.2.关于this对象

    每个函数在被调用时都会自动取得两个特殊变量: this arguments 。this引用的是函数执行的环境对象。

    • 在全局函数中, this 等于 window;
    • 当函数被作为某个对象的方法调用时, this 等于那个对象 ;
    • 匿名函数的执行环境具有全局性,因此其 this 对象通常指向 window (内部函数在搜索这两个变量时,只会搜索到其活动对象为止,因此永远不可能直接访问外部函数中的这两个变量 );

    var name = "The Window";
    var object = {
      name : "My Object",
      getNameFunc : function(){
        return function(){
          return this.name;
        };
      }
    };
    alert(object.getNameFunc()()); //"The Window"

    如果我们想返回‘My Object’可以这样写:

    var name = "The Window";
    var object = {
      name : "My Object",
      getNameFunc : function(){
          var that = this; //this指向object
        return function(){
          return that .name;
        };
      }
    };
    alert(object.getNameFunc()()); //"My Object"    

    4.3.内存泄露

    由于闭包会使得函数中的变量都被保存在内存中,内存消耗很大,所以不能滥用闭包。



     


     

     









     

     

  • 相关阅读:
    sqli-labs lexx25-28a(各种过滤)
    sqli-labs less-24(二次注入)
    sqli-labs less13-20(各种post型头部注入)
    sql注入之双查询注入
    sqli-labs less11-12(post型union注入)
    sqli-labs less8-10(布尔盲注时间盲注)
    sqli-labs less-7(文件读写)
    Vue ref childNode 坑
    Blob
    中文输入法不触发onkeyup事件的解决办法Script
  • 原文地址:https://www.cnblogs.com/yy136/p/11276879.html
Copyright © 2011-2022 走看看