JS闭包

作用域

在这里插入图片描述
当访问一个变量时,代码解释器会首先在当前的作用域查找,如果没找到,就去父级作用域去查找,直到找到该变量或者不存在父级作用域中,这样的链路就是作用域链。

作用域链: 即当前函数一般都会存在上层函数的作用域的引用,那么他们就形成了一条作用域链。

闭包

闭包其实就是一个可以访问其他函数内部变量的函数。即一个定义在函数内部的函数,或者直接说闭包是个内嵌函数也可以

闭包产生的原因
闭包产生的本质就是:当前环境中存在指向父级作用域的引用

function fun1() {
  var a = 2
  function fun2() {
    console.log(a);  //2
  }
  return fun2;
}
var result = fun1();
result();

闭包的表现形式

  1. 返回一个函数

  2. 在定时器、事件监听、Ajax 请求、Web Workers 或者任何异步中,只要使用了回调函数,实际上就是在使用闭包

    // 定时器
    setTimeout(function handler(){
      console.log('1');
    }1000);
    // 事件监听
    $('#app').click(function(){
      console.log('Event Listener');
    });
    
  3. 作为函数参数传递的形式

    var a = 1;
    function foo(){
      var a = 2;
      function baz(){
        console.log(a);
      }
      bar(baz);
    }
    function bar(fn){
      // 这就是闭包
      fn();
    }
    foo();  // 输出2,而不是1
    
  4. IIFE(立即执行函数),创建了闭包,保存了全局作用域(window)和当前函数的作用域,因此可以输出全局的变量

    var a = 2;
    (function IIFE(){
      console.log(a);  // 输出2
    })();
    

常见问题

// 以下代码执行会输出什么
for(var i = 1; i <= 5; i ++){
  setTimeout(function() {
    console.log(i)
  }, 0)
}

结果输出的是 5 个 6,那么一般面试官都会先问为什么都是 6?我想让你实现输出 1、2、3、4、5 的话怎么办呢?

  • setTimeout 为宏任务,由于 JS 中单线程 eventLoop 机制,在主线程同步任务执行完后才去执行宏任务,因此循环结束后 setTimeout 中的回调才依次执行。
  • 因为 setTimeout 函数也是一种闭包,往上找它的父级作用域链就是 window,变量 i 为 window 上的全局变量,开始执行 setTimeout 之前变量 i 已经就是 6 了,因此最后输出的连续就都是 6。

方式一【利用 IIFE(立即执行函数)】

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

方式二【使用ES6中的let】

for(let i = 1; i <= 5; i ++){
  setTimeout(function() {
    console.log(i)
  }, 0)
}

方式三【定时器传入第三个参数】

for(var i=1;i<=5;i++){
  setTimeout(function(j) {
    console.log(j)
  }, 0, i)
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值