闭包经典面试题

闭包应该是前段面试中经常碰到的面试题,很多人都会在这个问题上被问住。如果想要弄清楚就要掌握闭包的概念;

首先看面试题:

for (var i = 1; i <= 5; i++) {

  setTimeout( function timer() {

      console.log(i);

  }, 1000 );

}

上面的代码会输出什么?怎么 改动上述代码,使其依次输出1、2、3、4、5

答案是  程序会先输出一个"5",后面每隔1000毫秒输出一个6,

setTimeout()相关知识

在我们学习setTimeout的时候就知道,setTimeout有两个参数,第一个参数是回调函数,第二个参数是毫秒数,表示要执行回调函数所要延迟的时间。但我们还需要知道的是,setTimeout会返回一个Id,即这个定时器的Id,在上面的代码中其实已经创建了5个定时器,但是默认只返回了最后的一个Id,所以会先输出一个5。

接下来就该讨论为什么会输出5个数字6,而不是1、2、3、4、5了。

因为setTimeout()函数要等执行完函数调用栈中的代码,然后立即调用定时器。这是因为,我们的定时器都被放在了一个被称为队列的数据结构中,等待上下文的可执行代码运行完毕后,才开始运行定时器,也就是定时器才刚开始计时。所以在定时器的方法执行的时候,变量i已经变成了6,所以输出的全部是6。

那么怎么样才能输出1、2、3、4、5呢?

因为5个定时器所打印出来的是同一个i变量,所以想要实现输出不同的数字,就需要把每个定时器所访问的变量独立起来,这就用到了JavaScript的闭包。闭包用途很多,可以很好地区分开各个作用域,避免变量的混淆,但是滥用闭包也会导致性能问题。那么通过下面的修改就可以了;

for (var i = 1; i <= 5; i++) {

    (function(i){

        setTimeout( function timer() {

              console.log(i);

          },  1000 );

    })(i);

}

块级作用域--关键字let

使用闭包可以得到正确的结果,原因就是改变了i的作用域,那如果我们把循环中的每个setTimeout都独立成一个作用域是不是也能实现同样的结果呢?我们都知道,在JavaScript中,每个函数是一个独立的作用域,但是“{}”是不能形成独立作用域的。

在ES6中提出了一个新的关键字let,就可以声明一个仅对当前“{}”内部有作用的变量。输出的结果是一样。

for (let i = 1; i <= 5; i++) {

  setTimeout( function timer() {

      console.log(i);

  }, 1000 );

}




<think>我们正在处理关于JavaScript闭包面试题的用户查询。根据提供的引用,特别是引用[2]、[3]、[4],我们可以总结出几个常见的闭包面试题及其解析。用户要求的是“JavaScript闭包面试题”,因此我们将重点放在常见的闭包面试题上,并给出解析。根据引用[3]和[4]中的示例,有一个非常典型的闭包面试题,涉及一个函数返回一个对象,对象中有一个方法,该方法又调用外部函数,形成闭包。我们将以这个题目为例进行解析。题目如下(来自引用[4]):```javascriptfunctionfun(n,o){console.log(o);return{fun:function(m){returnfun(m,n);}};}```然后有如下调用:```javascriptvara=fun(0);//?a.fun(1);//?a.fun(2);//?a.fun(3);//?varb=fun(0).fun(1).fun(2).fun(3);//?varc=fun(0).fun(1);//?c.fun(2);//?c.fun(3);//?```我们将逐步分析这个题目。首先,函数`fun`接受两个参数`n`和`o`,并打印出`o`。然后返回一个对象,该对象有一个方法`fun`,这个方法接受一个参数`m`,并调用`fun(m,n)`(注意这里调用的是外部的`fun`函数,并且将当前的`n`作为第二个参数传入,即新的调用中`o`将是当前的`n`)。分析关键点:1.每次调用外部函数`fun`时,都会创建一个新的闭包,该闭包保存了此次调用时的参数`n`(因为内部函数引用了`n`)。2.内部函数`fun`(即返回对象的方法)被调用时,会使用闭包中保存的`n`作为下一次调用的第二个参数(即`o`)。现在逐行分析:第一部分:```javascriptvara=fun(0);//调用fun(0),此时n=0,o未传入(即undefined),所以打印undefined//返回一个对象,这个对象的fun方法被调用时,会调用fun(m,0)(因为这里的n是0)//将返回的对象赋给aa.fun(1);//调用a.fun(1)->即调用fun(1,0)(因为闭包中保存的n是0)//此时,在fun(1,0)中:n=1,o=0->打印0//然后返回一个新对象,这个新对象的fun方法被调用时,会调用fun(m,1)(因为这里的n是1)a.fun(2);//同样,调用a.fun(2)->调用fun(2,0)(注意:a是第一次调用返回的对象,其内部闭包保存的n是0)//所以打印0a.fun(3);//同理,打印0```所以第一部分的输出是:`undefined,0,0,0`第二部分:```javascriptvarb=fun(0).fun(1).fun(2).fun(3);//分解://1.fun(0):打印undefined,返回对象A(对象A的fun方法调用时会用n=0)//2.对象A.fun(1):调用fun(1,0)->打印0,返回对象B(对象B的fun方法调用时会用n=1)//3.对象B.fun(2):调用fun(2,1)->打印1,返回对象C(对象C的fun方法调用时会用n=2)//4.对象C.fun(3):调用fun(3,2)->打印2//所以输出:undefined,0,1,2```第三部分:```javascriptvarc=fun(0).fun(1);//1.fun(0):打印undefined,返回对象A//2.对象A.fun(1):调用fun(1,0)->打印0,返回对象B(此时对象B的闭包中n=1)//将对象B赋给cc.fun(2);//调用c(即对象B)的fun(2)->调用fun(2,1)->打印1c.fun(3);//调用c的fun(3)->调用fun(3,1)->打印1```所以第三部分的输出:`undefined,0`(来自`varc=...`),然后`1,1`(来自`c.fun(2)`和`c.fun(3)`)。因此,整个题目的输出为:```undefined000------------undefined012------------undefined011```这个题目考察了闭包的概念:内部函数可以访问外部函数的变量,并且每次调用外部函数都会生成一个新的闭包环境。除了这个题目,闭包面试题还可能涉及循环中的闭包闭包的作用域链、闭包的内存管理等。例如,循环中使用闭包经典问题(来自引用[1]和[2]):```javascriptfor(vari=0;i<5;i++){setTimeout(function(){console.log(i);},1000);}```这段代码会输出5个5,因为setTimeout中的函数共享同一个闭包,而变量i是全局作用域(或函数作用域)中的,循环结束后i变成了5。解决方法通常是使用立即执行函数表达式(IIFE)或使用let声明块级作用域变量。根据用户的需求,我们提供以上两个典型的闭包面试题及其解析。现在,我们按照要求生成相关问题。</think>### JavaScript 闭包面试题解析 #### 典型闭包面试题分析(引用[3][4]) ```javascript function fun(n, o) { console.log(o); return { fn: function(m) { return fun(m, n); } }; } // 示例调用 var a = fun(0); // 输出:undefined a.fn(1); // 输出:0 a.fn(2); // 输出:0 a.fn(3); // 输出:0 var b = fun(0).fn(1).fn(2).fn(3); // 输出:undefined, 0, 1, 2 var c = fun(0).fn(1); // 输出:undefined, 0 c.fn(2); // 输出:1 c.fn(3); // 输出:1 ``` #### 闭包机制解析 1. **闭包形成原理** 函数`fun`返回的对象中包含内部函数`fn`,该函数引用了外部变量`n`,形成闭包。每次调用`fun`
评论 23
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值