作用域和闭包

20 篇文章 0 订阅

一说到javascript里的闭包,大部分人都好像有所耳闻,但又说不太清楚到底是个什么东西。那么闭包到底是何方神圣呢?

闭包是javascript中非常常见,但是又非常神秘的一个概念,有多少人像我一样,看了好多关于闭包的技术文章,却始终没能彻底搞懂闭包的深层次原理,经常是面试前突击一下闭包相关的文章,似懂非懂,过段时间后又几乎全忘了,只留下些残破的似是而非的记忆。

今天我尝试着来总结一下我自己对于闭包的理解,加深理解。

先来看一段非常常见的代码:

  function getSomeFoods(cb) {
    let a = 4;
    this.$api('getFoods').get({ // http请求
      id: 123456777834
    }).then(res => {
      if(res) {
        cb(res,a)
      }
    })
  }
  getSomeFoods(function(res, a) {
    console.log(a) // 4
  })

这是一个http请求函数,请求成功后执行cb回调函数,并将getSomeFoods函数内的a传递出来,并在定义a的函数之外打印或者调用,这样就产生了闭包。

还有个经典的闭包案列:

  function goods() {
    let a = 4;
    function bad() {
      console.log(a)
    }
    return bad;
  }
  let badz = goods();
  badz(); // 4

goods函数返回一个内部函数,并且在其它地方调用,就形成了闭包。

闭包的定义:一个函数内部的函数,在其词法作用域之外的其它地方被调用,该函数对自己词法作用域的引用称为闭包。

什么是词法作用域?和作用域一个意思,只是比较正式的一种说法。

定义:词法作用域就是定义在词法阶段的作用域,你在写代码时将变量和块作用域写在哪里决定了词法作用域。

回到上面的例子,bad()函数的词法作用域是整个goods()函数内部,所以bad()函数可以访问到a,当我们在外部调用badz()时其实是在执行bad()函数,此时已经是在bad()函数的词法作用域之外调用了,而函数可以正常执行,并且访问到了a,这就是形成了闭包效果。

一般情况下,一个函数执行完了我们都会希望该函数的内部作用域被销毁,释放内存,引擎也确实有一套垃圾回收机制来做这件事,但是如果这个函数我想多次引用,不希望被销毁的时候,闭包就能帮上忙了。

good()函数虽然执行了,但他返回了一个bad()函数,可以重复调用,那么good的作用域就会一直存在,没有被回收。

闭包经典案例

  for(var i=0;i<5;i++) {
    setTimeout(() => {
      console.log(i)
    },i*1000)
  }

我们期望的结果是打印出0~4五个值,但实际输出的是5个5,为什么呢?

先来理一下,循环的终止条件是i>=5,那么当i=5的时候就会终止,循环的执行是很快的,所以当i已经结束循环了延迟函数都还没有执行,而var定义的变量是一个全局变量,i至始至终都是同一个i,但是值却一直在变化,最后加到5才停下,所以执行延迟函数的时候打印的是同一个i且等于5。

原因找到了,那么要想得到期望的结果就有思路了。因为是同一个i导致的错误,那么我们把每一次的i都单独存一份起来,等到延迟函数执行的时候去拿对应的副本不就可以了么?

这不正式闭包的强项么?利用闭包每次生成一个新的作用域,使得延迟函数的回调可以将新的作用域封闭在每个迭代内部,延迟函数执行的时候就能拿到正确的值。

  for(var i=0;i<5;i++) {
    (function(j) {
       setTimeout(() => {
         console.log(j)
       },j*1000)
    })(i)
  }

每次循环都执行以下内部的立即执行函数,并把i当参数传递进去,这样就能正常工作啦。

每次迭代创建一个新的作用域等同于需要一个块作用域。es6的let声明有劫持作用域的功能,这里也可以用到:

  for(let i=0;i<5;i++) {
    setTimeout(() => {
      console.log(i)
    },i*1000)
  }

let声明有一个特殊功能,循环的时候不止被声明一次,每次迭代都会声明,随后的每次迭代都会用上一个迭代结束时的值来初始化这个变量。

这就是我学习和理解的闭包。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值