标签搜索

作用域和闭包的深入理解

cicaba
2018-03-12 / 0 评论 / 4 阅读 / 正在检测是否收录...

当函数可以记住并访问所在的词法作用域时,就产生了闭包,即使函数是在当前词法作用
域之外执行。
我来对这个定义进行解释下
函数要在执行后不被垃圾回收机制回收,并且可以访问当前作用域中的属性和方法. 函数在当前作用域外也可以被调用!

let message = "Cicaba"
function foo(){
  console.log(message)  //是一个 RHS 引用查询
}
foo()//Cicaba

上面代码并不是真正意义上的闭包.
函数内部作用域只是引用了函数外部作用域的变量.(只是作用域链的一种体现)

function foo() {
  var a = 2;
  function baz() {
    console.log( a ); // 2
  }
  bar( baz );
}
function bar(fn) {
  fn(); // 妈妈快看呀,这就是闭包!
}

闭包和作用域链有着密切的关系.闭包就是突破作用域链的一种方式!
看一个金典的例子:

for(var i = 0; i<6; i++){
  setTimeout(function timer(){
    console.log(i)
  },1000)
}

上面代码每隔一秒会输出6个5,为什么乐?
要理解这个问题必须你先理解词法作用域(作用域)
我们来看上面代码发生了什么.
for循环中定义了一个全局变量i, 每次循环都在改变i的值.
而setTimeout是在循环结束后才被执行(延迟回调函数(异步执行)).
每次循环都在当前作用域声明一个定时器函数, 都共享一个作用域里的资源, 所以打印了6个5.
解决办法

for(var i = 0; i<6; i++){
let j = i;
  setTimeout(function timer(){
    console.log(j)
  },1000)
}

解决思路在每次循环中创建一个词法作用域,

for(let i = 0; i<6; i++){
  setTimeout(function timer(){
    console.log(i)
  },1000)
}
//for循环的let声明, 会有一个特殊的行为, 会在每次循环迭代时重新声明, 并且会把上次迭代的值初始化当前变量!
0

评论 (0)

取消