• 当函数能够"记住"并访问所在的词法作用域时,就产生了闭包,即使函数是在当前词法作用域之外执行。
function foo () {
  var a = 2;
  function bar (){
      console.log(a)
  }
  return bar ;
}
var baz = foo()
baz () // 2 这里就是闭包的效果
  • foo()执行后,其返回值(bar()函数)赋值给变量 baz 并调用 baz(),实际上是只通过不同标识符来引用 调用了内部函数 bar()
  • 在 foo()执行后,通常会期待 foo()整个内部作用域被销毁,因为 js 引擎有垃圾回收器,用来释放不再使用的内存空间。由于 foo()的内容看上去不会再被使用,所以很自然的会考虑对它进行回收
  • 而闭包阻止了 js 引擎的垃圾回收机制,foo()内部作用域依然存在,没有被回收,bar()本身在使用这个内部作用域。
  • bar()对该作用域的引用,这个引用就叫闭包

闭包:函数在定义时的词法作用域外被调用,闭包使得函数可以继续访问定义时的词法作用域.

一个经典的循环和闭包

for (var  i = 0; i <= 5; i++) {
console.log(i,'====')

  setTimeout(() => {
      console.log(i )
  }, i *1000);
}

这里会每隔1s打印1个6,而不是所期待的每隔1s 从0 打印到 5

原因是:for循环属于同步,setTimeout属于异步,同步代码执行完才会去执行异步,也就是上面代码每次循环都会项任务队列里加一个定时器,等for循环结束后开始执行,这时候的i是6,所以会每隔1s打印1个6。在for循环中用var就是一个全局的i,所以每次会被后面的给替换

  • 如果将var换为let,因为let会劫持作用域,js引擎检测到定时器会将它放到队列中,附带上let劫持 的这个作用域中的i值,每次迭代,let都会根据上一个i值,重新再生成一个i(也就是说在每次进入循环的时候都会把之前的i赋值给一个新的i)

爬。