你这篇速识 js闭包 保熟吗?我一写博客的能写生瓜蛋子?

举报
北极光之夜。 发表于 2021/08/11 21:40:38 2021/08/11
【摘要】 一.速识闭包: 闭包可谓是js里很常见的,所以今天带大家快速了解闭包的形式。直接说概念枯燥难懂,直接通过下面两个小例子迅速了解闭包(还有一点很重要的就是,🍉不熟别生气,下篇文章肯定熟🤣):👉1.首先思考下面这个简单的小例子,执行quote()函数,它 console.log(a) 打印的 a 到底是我们在局部函数里定义的 a=666 还是在全局里面定义的 a=888 呢? // 1...

一.速识闭包:

 闭包可谓是js里很常见的,所以今天带大家快速了解闭包的形式。直接说概念枯燥难懂,直接通过下面两个小例子迅速了解闭包(还有一点很重要的就是,🍉不熟别生气,下篇文章肯定熟🤣):👉

1.首先思考下面这个简单的小例子,执行quote()函数,它 console.log(a) 打印的 a 到底是我们在局部函数里定义的 a=666 还是在全局里面定义的 a=888 呢?

  // 1.定义一个test函数
      function test() {
           // 2.函数内部声明一个变量 a 为 666
          const a = 666;
           // 3.return返回一个函数
          return function () {
            console.log(a);
        };
      }
      // 4.定义一个quote,给他赋值为test(),可知test()返回的是一个函数,所以它也是函数
      const quote = test();
      // 5.在全局同样定义一个变量 a 为 888
      const a = 888;
      // 6.执行quote
      quote();

  答案是输出 666 ,这是因为quote函数会在它定义的地方向上一层去查找它需要的变量,queto函数虽然是在全局里面直接执行,但是注意了,它实际上是在test函数里面定义声明的,所以,它会先在test函数内部看看有没有 a ,而test函数里面定义有,所以直接获取,输出 666 。好,先这样,继续往下看第2个小例子:
在这里插入图片描述

2.再次思考下面这个简单的小例子,这次是在全局定义了一个quote函数,我将它作为参数传入 test函数,然后是在 test 函数内部执行,这次console.log(a) 打印的 a 到底是我们在局部函数里定义的 a=666 还是在全局里面定义的 a=888 呢?

      //1.全局声明一个变量 a 为 888
      const a = 888;
      // 2.定义一个函数quote
      function quote() {
        console.log(a);
      }
      // 3.定义一个test函数,接收一个参数 fn ,这个参数将为函数
      function test(fn) {
        // 4.函数内部声明一个变量 a 为 666
        const a = 666;
        // 5.执行 fn 函数
        fn();
      }
      //6.调用 test函数,同时将quote作为参数传进去
      test(quote);

  答案是输出 888 ,还是一样的原因,quote函数会在它定义的地方向上一层去查找它需要的变量,虽然quote函数是在test函数里面被赋值给参数后在里面那执行的,但是quote是在全局里声明定义的,所以,它会向上找就是在全局里看看有没有a ,全局里有 ,且为 888 ,所以直接获取,输出 666。

在这里插入图片描述
  上面两个例子其实就闭包的应用,总结起来就是 ----->;闭包是一个函数和它周围状态的引用捆绑起来一起的组合。 第一个小例子中和quote函数捆绑的是test函数里定义的 a ,第二个小例子中和quote函数捆绑的是全局里定义的 a 。它们就是闭包。其次还有重要的一点就是----->闭包和函数的定义有关。 它和this不同,this是和函数的执行方式有关。

二.闭包常见形式:

1.第一种就是直接在函数里return返回一个函数:

function test() {
    const a = 666;
    return function () {};
  }

2.第二种可以是通过变量表达式定义函数,再return返回变量:

   function test() {
        const n = function () {};
        return n;
      }

3.还可以是先在全局定义变量,再在函数内部给他赋值为一个函数:

    var quote;
      function test() {
        var a = 666;
        quote = function () {
          console.log(a);
        };
      }

4.通过函数的参数的形式:

   function test(fn) {
        fn();
      }
      function quote() {
        var a = 666;
        var n = function () {
          console.log(a);
        };
        test(n);
      }
      quote();

  等等等,闭包形式还有很多种就不一一列举了。只需要记住闭包是一个函数和它周围状态的引用捆绑起来一起的组合。

三.扩展点:

分析下面这个程序,最后将会打印出什么?

    function fn() {
        var arr = [];
        for (var i = 0; i < 6; i++) {
          arr[i] = function () {
            console.log(i);
          };
        }
        return arr;
      }
      
      var quote = fn();
      
      for (var i = 0; i < 6; i++) {
        // 立即执行函数
        quote[i]();
      }

  答案是输出 6个6,因为在 fn 函数里,其实通过循环给arr数组每一个元素赋值的时候只是赋值为空函数,并没有引用 i ,当最后执行的时候,回去看闭包函数定义的地方往上找 i ,i 早已循环过6次,值已经为 6 了,所以数组的每一个函数得到的 i 都是 6 。

在这里插入图片描述
如何让数组的元素得到的是依次递增的 i 呢,有下面两种方法:

1.将 fn 函数里的 for 循环里的 var i 改为 let i :

   function fn() {
        var arr = [];
        for (let i = 0; i < 6; i++) {
          arr[i] = function () {
            console.log(i);
          };
        }
        return arr;
      }
      var quote = fn();
      for (var i = 0; i < 6; i++) {
        quote[i]();
      }

看结果,数组里每个元素得到的是依次递增的 i 了,这是因为因为let在代码块中都有自己的作用域,所以在for循环中的表达式中使用let它的每一个值都会单独存在一个独立的作用域中不会被覆盖掉。

在这里插入图片描述
2.在 fn 函数里循环里赋值的时候通过立即执行函数引了i,将已经引用的 i 通过参数传给数组元素:

function fn() {
    var arr = [];
    for (let i = 0; i < 6; i++) {
      arr[i] = (function (j) {
        return function () {
          console.log(j);
        };
      })(i);
    }
    return arr;
  }
  var quote = fn();
  for (var i = 0; i < 6; i++) {
    quote[i]();
  }

一样的结果:

在这里插入图片描述

四.总结:

到这里就结束啦,概括起来就是闭包是一个函数和它周围状态的引用捆绑起来一起的组合。
如果文中有什么错误的地方恳请大佬指出~
下次见啦~😉

在这里插入图片描述

【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息, 否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。