zoukankan      html  css  js  c++  java
  • JavaScript中的匿名函数、立即执行函数和闭包

    匿名函数是没有函数名的,不能单独使用;

    立即执行函数是基于匿名函数实现的,也没有函数名,会在定义后立即执行;

    闭包是有权访问另一个函数作用域中的变量的函数。匿名函数、立即执行函数只要满足 有权访问另一个函数作用域中的变量 这一个条件,就成了闭包。

    匿名函数

    匿名函数:没有函数名的函数

    匿名函数不能单独定义与使用

    function foo() {
      console.log('普通函数');
    }
    // 去掉函数名 foo 
    function () { // SyntaxError: Function statements require a function name
      console.log('匿名函数');
    }
    
    匿名函数的应用场景
    • 用于函数表达式
    • 作为返回值
    • 用于定义对象方法
    • 作为回调函数
    • 用于立即执行函数
    • 用于DOM元素注册事件
    • 其他 ...

    用于函数表达式

    var sum = function (num1, num2) {
      return num1 + num2;
    };
    console.log(sum(2, 3)); 
    

    作为返回值

    function sum(sum1, sum2) { 
    
      return function() {
        return sum1 + sum2;    
      }
    
    }
    console.log(sum(2, 3));    // [Function]
    console.log(sum(2, 3)());  // 5
    

    用于定义对象方法

    var obj = {
      name: 'uakora',
      age: 27,
      foo: function() {
        console.log(this.name + ' ' + this.age);
      }
    };
    obj.foo(); // uakora 27 
    

    作为回调函数

    setTimeout(function() {
      console.log('匿名函数作为回调函数');
    }, 1000);
    

    用于立即执行函数

    (function() {
      console.log('立即执行函数是基于匿名函数创建的');
    }());
    

    用于DOM元素注册事件

    <input type="button" value="Click me!" id="btn">
    <script>
        var btn = document.querySelector("#btn");
        //给按钮注册点击事件
        sub.onclick = function(){
            console.log('Click event');
        }
    </script>
    

    立即执行函数

    立即执行函数(IIFE,Immediately-Invoked Function Expression),是一种在定义后就会立即执行的函数,其实质是一种语法。

    立即执行函数的形式

    两种常用形式

    常用形式一:将匿名函数包裹在一个括号运算符中,后面再跟一个括号

    (function () {
      console.log('立即执行函数');
    })(); // !!!特别说明:若此立即执行函数后面立马又跟着一个立即执行函数,一定要在结尾加分号,否则后面的立即执行函数会报错!
    
    // 上一个立即执行函数不加分号,下行代码将报错:TypeError: (intermediate value)(...) is not a function
    (function (a, b, c) { // 形参
      console.log(a + b + c);  // 6
    })(1, 2, 3); // 实参
    

    常用形式二:匿名函数后面跟一个括号,再将整个包裹在一个括号运算符中

    (function () {
      console.log('立即执行函数');
    }());
    
    (function (a, b, c) { // 形参
      console.log(a + b + c);  // 6
    }(1, 2, 3)); // 实参
    

    其他形式

    可以用 !、+、-、~ 运算符代替常用形式一中的第一个括号

    !function (a, b, c) { 
      console.log(a + b + c);  // 6
    }(1, 2, 3);
    
    +function (a, b, c) { 
      console.log(a + b + c);  // 6
    }(1, 2, 3);
    
    -function (a, b, c) { 
      console.log(a + b + c);  // 6
    }(1, 2, 3);
    
    ~function (a, b, c) { 
      console.log(a + b + c);  // 6
    }(1, 2, 3);
    
    立即执行函数的作用

    立即执行函数最本质的作用是:创建一个独立的作用域。利用这一功能,可以

    • 初始化数据和页面(只执行一次)
    • 模块化开发中,定义私有变量,防止污染全局(独立作用域)
    • 解决闭包中的状态保存问题;(常见的一个函数内部返回多个函数,调用这些函数,打印父函数内部变量的问题)

    案例分析

    var liList = document.getElementsByTagName('li');
    for(var i = 0; i < liList.length; i++) {
      liList[i].onclick = function() {
        alert(i); // 结果:alert 出来的总是 6, 而不是0、1、2、3、4、5
      }
    }
    

    期望结果:0、1、2、3、4、5
    实际结果:alert 出来的总是 6
    原因分析:没有形成独立作用域,每次点击执行函数都是去访问同一个 i 值(且是循环结束后的值 6)
    解决方案:使用立即执行函数为每个 li 元素的点击事件函数创建独立作用域

    var liList = document.getElementsByTagName('li');
    for (var i = 0; i < 6; i++) {
      (function (i) {
        liList[i].onclick = function () {
          alert(i); // 0、1、2、3、4、5
        }
      })(i);
    }
    

    闭包

      闭包是指有权访问另一个函数作用域中的变量的函数。创建闭包的常见方式,就是在一个函数内部创建另一个函数。闭包可以有函数名,也可以是匿名函数(没有函数名);

    闭包形式一:直接以匿名函数的形式作为外部函数的返回值(普遍用法)

    function outer() {
      var n = 1;
    
      return function() {
        n++; // 访问 outer 函数作用域中的变量 n,形成闭包
        console.log(n);
      }
    }
    
    outer()();
    

    闭包形式二:在外部函数内定义一个内部函数,并返回内部函数名

    function outer() {
      var n = 1;
    
      function inner() {
        n++; // 访问 outer 函数作用域中的变量 n,形成闭包
        console.log(n);
      }
    
      return inner;
    }
    
    outer()();
    

    闭包形式三:在外部函数内定义一个立即执行函数

    function outer() {
      var n = 1;
    
      (function() {
        n++; // 访问 outer 函数作用域中的变量 n,形成闭包
        console.log(n);
      })();
    
    }
    
    outer();
    
    闭包的作用

    作用一:可以直接访问函数作用域中的变量
    作用二:可以将函数作用域中的变量如全局变量一样始终保存在内存中 (其实能否一直保存局部变量,跟闭包的执行形式有关

    说明:作用二的解析为个人理解

    以下示例为证明观点:闭包能否一直保存局部变量,跟闭包的执行形式有关

    闭包执行方式一:在返回的闭包后面紧跟一个 () ,立即执行

    function outer() {
      var n = 1;
    
      function inner() {
        n++;
        console.log(n);
      }
    
      return inner;
    }
    
    outer()();  // 2
    outer()();  // 2  
    

    闭包执行方式二:将返回的闭包赋值给一个全局变量,全局变量运行 () 操作,执行闭包

    function outer() {
      var n = 1;
    
      function inner() {
        n++;
        console.log(n);
      }
    
      return inner;
    }
    
    var inner = outer();
    inner();    // 2
    inner();    // 3  (是在 2 的基础上加 1)
    

    以上两种执行方式单独使用,只能说明 第二种执行方式,能使闭包一直保存局部变量,但还不能证明闭包的第一种执行方式不能一直保存局部变量,因为每次执行 outer()函数时,变量 n 都被重新初始化了。

    请继续看以下代码,同时使用了闭包的两种执行方式:

    function outer() {
      var n = 1;
    
      function inner() {
        n++;
        console.log(n);
      }
    
      return inner;
    }
    
    var inner = outer();
    
    outer()();  // 2
    outer()();  // 2  (Flag1)
    
    inner();    // 2  (Flag2)
    inner();    // 3
    

    可以看到,Flag1 跟 Flag2 的值都是 2,假设 outer()() 这样的闭包执行方式会一直保存局部变量在内存的话,那 Flag2 处的值应该是 3。

    显然假设不成立。

    从而说明:闭包并非就一定可以一直保存局部变量在内存,还跟执行方式有关

    主动释放内存
    function outer() {
      var n = 1;
    
      function inner() {
        n++;
        console.log('n = ', n);
      }
    
      return inner;
    }
    
    var inner = outer();
    inner();    // 2  
    inner();    // 3
    
    inner = null;  // 解除对闭包的引用,以便释放内存
    
    闭包的应用场景
    • 保护函数局部变量的安全
    • 在内存中维持一个变量
    • 通过保护变量的安全实现JS私有属性和私有方法(不能被外部访问)

    使用闭包的注意点

      由于闭包会携带 包含它的函数 的作用域,因此会比其他函数占用跟多的内存。过度使用闭包可能会导致内存占用过多

  • 相关阅读:
    聊聊自动化的打开方式
    浅谈如何提高自动化测试的稳定性和可维护性 (pytest&allure)
    Jmeter做压力测试的心得
    为什么测试人员必须掌握Linux?
    数据结构与算法系列 目录
    高可用高性能分布式文件系统FastDFS进阶keepalived+nginx对多tracker进行高可用热备
    Fastdfs文件系统扩容
    Linux下FastDFS分布式存储-总结及部署记录
    How to setup a Alpine Linux mirror
    修改内核参数开启转发
  • 原文地址:https://www.cnblogs.com/uakora/p/12695429.html
Copyright © 2011-2022 走看看