• 函数作用域查找、闭包和匿名函数整理


    一、函数作用域查找

    1、定义说明
    1)、函数当前作用域查找不到,可以访问外层函数作用域的活动对象(参数、局部变量、定义在外层函数体里的函数)
    2)、外层的外层函数。。。一直到全局

    第一条说明:定义在外层函数体里的函数,包括当前函数,当前函数调用自己的时候,就是递归调用。

    2、原理
    执行环境、作用域链、作用域、活动对象
    1)、调用内层函数,会创建一个执行环境,执行环境会关联一个作用域链
    2)、调用内层函数时,所有的外层函数都已经调用完毕或者外层函数调用中,所有只要把所用外层函数作用域(包括最外层全局)的活动对象,关联到当前内层函数的作用域链上。
    3)、最后创建内层函数作用域的活动对象,并且关联到作用域链的最前端。

    活动对象注释:
    函数参数,函数体里面定义的局部变量,函数体里面定义的函数

    3、代码示例

    var str1 = '全局变量';
    function func(arg) {
      var str2 = '外层局部变量';
      function funcInner1() {
        console.log('外层函数的其他函数');
      }
      function funcInner2(argInner2) {
        var str3 = '内层函数变量';
        console.log(str3);
        console.log(argInner2);
    
        console.log(arg);
        console.log(str2);
        funcInner1();
        console.log(str1);
      }
      return funcInner2;
    }
    var result = func('外层函数参数')
    result('内层函数参数');
    
    执行结果:
    内层函数变量
    内层函数参数
    外层函数参数
    外层局部变量
    外层函数的其他函数
    全局变量

    二、闭包

    1、定义
    定义在函数体里的内部函数,这些内部函数可以访问它们所在的外部函数中声明的所有局部变量、参数和声明的其他内部函数。当其中一个这样的内部函数在包含它们的外部函数之外被调用时,就会形成闭包。

    2、通俗解释
    内层函数作为外层函数执行结果被返回,当外层函数执行完毕后,定义在其内部的局部变量、参数和声明的其他内部函数并没有被回收,而是可以通过返回的函数的执行被获取,即形成了闭包。

    3、形成闭包的条件
    1)、嵌套函数
    2)、内层函数访问外层函数的活动对象
    3)、内层函数子在外层函数之外的地方被调用

    第三条件解释:一般是内层函数作为外层函数的返回值,外层函数之外的地方,可以先调用外层函数,拿到对应返回值,再通过返回值调用内层函数。

    4、特点:
    1)、作为一个函数变量的一个引用,当函数返回时,其处于激活状态。
    2)、一个闭包就是当一个函数返回时,一个没有释放资源的栈区。

    5、原理
    函数作用域查找

    6、代码示例

    function outer() {
      var scope = 10;
      return function inner() {
        scope += 10;
        console.log(scope);
      }
    }
    var fn = outer();
    fn();
    
    执行结果:
    20
    //inner函数作为outer函数执行结果被返回,当outer函数执行完毕后,定义在其内部的变量scope并没有被回收,而是可以通过函数fn的执行被获取,这里的inner函数,即形成了闭包。

    7、好处:
    1)、变量长期驻扎在内存中;
    2)、避免全局变量的污染;

    避免全局变量的污染说明:
    上面代码示例中,我们也可以把scope定义成全局变量,但是这样我们就没法控制仅允许inner函数可以修改scope的值,因为全局变量,所有地方都可以访问。

    8、坏处:
    1)、内存消耗
    通常来说,函数的活动对象会随着执行期上下文一起销毁,但是,由于闭包引用另外一个函数的活动对象,因此这个活动对象无法被销毁,这意味着,闭包比一般的函数需要更多的内存消耗。

    进一步说明:尤其在IE浏览器中需要关注。由于IE使用非原生javascript对象实现DOM对象,因此闭包会导致内存泄露问题。
    2)、性能问题
    使用闭包时,会涉及到跨作用域访问,每次访问都会导致性能损失。

    进一步说明:因此在脚本中,最好小心使用闭包,它同时会涉及到内存和速度问题。不过我们可以通过把跨作用域变量存储在局部变量中,然后直接访问局部变量,来减轻对执行速度的影响。

    10、闭包常用案例
    1)、延迟输出
    2)、累加器

    代码1:
    //延迟输出
    for (var i = 0; i < 5; ++i) {
      (function (i) {
        setTimeout(function () {
          console.log(i + ' '); 
        }, 100); 
      })(i);
    }
    
    执行结果:
    0 
    1 
    2 
    3 
    4
    
    代码2:
    //累加器
    //initNum: 初始值, step: 步长
    function getTotalizer(initNum, step) {
      var num = initNum;
      return function() {
        num = num + step;
        return num;
      }
    }
    
    var add = getTotalizer(10, 5);
    console.log(add());
    console.log(add());
    console.log(add());
    console.log(add());
    
    执行结果:
    15
    20
    25
    30

    三、匿名函数

    1、定义:
    匿名函数,就是没有函数名的函数

    2、三种调用方法
    1)、可以定义一个匿名函数,并且立即调用。
    2)、可以定义一个匿名函数,赋值给一个变量,通过这个变量调用函数。
    3)、匿名函数作为函数返回值返回,通过返回值调用匿名函数。

    3、匿名函数与闭包关系
    1)、匿名函数和闭包没有实际关系
    2)、闭包的条件,需要返回一个内层函数,这个内层函数访问外层函数的活动对象,这个内层函数在外层函数之外的地方被调用。
    3)、上面内层函数只能通过函数的返回值被调用,不能通过方法名直接调用,这种情况下,把这个内层函数声明成匿名函数更合理,通常也是这么用的。

    4、代码示例

    //1、通过表达式自我执行
    (function() {
      console.log('执行');
    })();
    
    //2、将匿名函数赋给变量
    var result = function () { 
      console.log('执行');
    };
    result();
    
    //3、匿名函数作为函数返回值
    function func() {
      var i = '局部变量i';
      return function () {     
        return i;
      };
    }
    console.log(func()());
  • 相关阅读:
    Scrapy中的反反爬、logging设置、Request参数及POST请求
    scrapy的CrawlSpider类
    利用scrapy爬取腾讯的招聘信息
    scrapy知识补充--scrapy shell 及Spider
    scrapy的一个简单小项目
    scrapy框架介绍及安装
    并发编程--greenlet与gevent
    并发编程--协程
    并发编程--进程池与线程池-练习3
    并发编程--进程池与线程池-练习2
  • 原文地址:https://www.cnblogs.com/bravo2012/p/11288090.html
Copyright © 2020-2023  润新知