首页 > javascript关于闭包的面试题

javascript关于闭包的面试题

javascriptfunction f1(){
    var n=999;
    nAdd=function(){n+=1}
    function f2(){
      alert(n);
    }
    return f2;
  }
  var result=f1();
  result(); // 999
  nAdd();
  result(); // 1000

在这段代码中,result()它一共运行了两次,第一次的值是999,第二次的值是1000。这证明了,函数f1中的局部变量n一直保存在内存中,并没有在f1调用后被自动清除。为什么会这样呢?尤其是第二次,为何输出的不是999呢?
nAdd=function(){n+=1}又起到了什么作用呢?


1 并没有在f1调用后被自动清除。为什么会这样呢?
答:没有清除正因为闭包的机制导致的。延长变量的作用域和生命周期

首先f1 函数的作用域链。1 指向全局,2 指向自身。自身里面包括,n=999 f2, nAdd这个函数没有加var声明,是全局变量。但是里面的n引用的是f1内部的n=999

然后 f2函数的作用域链。1 指向全局 2 指向f1的作用域 3,指向自己作用域

nAdd这个作用域链 1 指向全局,2 持有n=999引用,(我觉得也指向f1作用域),3 指向自己作用域

执行 var result=f1(); 这个楼主应该清楚,就是result就是指向f2函数
调用 result();弹出n.就是先去自己作用域找。没有。然后就去上级作用域f1里找。找到了。n=999,弹出999
执行nAdd(),这时,去自己作用域里找。没有n,所以去上级作用域。f1里。发现n,执行n=n+1,n变1000
再次执行result();和上一次一样。就是先去自己作用域找。没有。然后就去上级作用域f1里找。找到了 这时n=1000了。所以弹出1000

推荐楼主看<<javascript高级程序设计第三版>>里面讲解作用域链,楼主就明白了。
我可能说的不是十分准确。


这是闭包最基础的问题。
https://github.com/getify/You-Dont-Know-JS
把这里面的closure看完你就能明白闭包了。

keywords:垃圾回收,作用域链。


还是闭包,好无聊。


首先要说的是,闭包是functional language里面的核心概念。
当出现高阶嵌套函数的时候,编译器会做closure convention闭包变换,核心就是变量不在分配在stack上,而是分配在heap上。这就是为什么f1已经返回,但是n还能被+1的原因。
楼主给出的这个程序,实际上就是一个高阶嵌套函数。
1. 因为在函数里面有定义的函数,这是嵌套。pascal也是允许嵌套函数。
2. 高阶的原因是,函数可以所谓参数传递和返回,像我们熟悉的C语言。
但是当高阶和嵌套同时出现,就会造成麻烦,所以pascal和C都只能支持其中的一个。

我来分析一下这个程序的执行流。
1. var result=f1(); 返回了一个函数f2, 因此result为f2。这个高阶函数特性,参考C语言函数指针。
2. result(); 调用f2,显然输出999.
3. nAdd(); 这里需要注意,这个nAdd实际上在定义的时候是一个lambda,是一个匿名函数,功能是n+=1。定义时将这个函数赋值给nAdd。所以在此时,实际上是调用了n+=1.为什么能找到n?因为n在堆里面。
4. result(); 调用f2,显然输出1000.

最后一点,n在堆上如何被销毁,这个工作是垃圾收集器负责。当n不在被任何闭包的env引用的时候,会被回收。


因为在当你运行nAdd();时,当前域并没有找到n这个变量,便向上寻找n变量,而f1()父函数里存在n=999,便吧999+1=1000,这里的n已经改变了,所以当你下次运行f2()时,就会弹出1000。你可以看看http://www.hubwiz.com/exchange/556c112bd75ed54c3bee9dbb


其实这里的考点是2个。
1、闭包。2、作用域。
nAdd=function(){n+=1}虽然是写在函数内部的,但是并没写var,所以是指向window的全局函数。但是定义在函数内部,所以可以调用n。


1)关于闭包,就是变量n的问题
2)关于函数声明和函数定义表达式
声明语句定义的表达式其作用域在其声明所在的函数范围内,所以f2是一个局部函数,要访问它必须通过其嵌套函数进行,而nAdd通过一个函数表达式声明的变量,前面没有var关键字所以是个全局变量,函数对象在f1执行完后不会被垃圾回收,不通过f1也可以访问到
3)因nAdd其在函数中定义的,所以具有闭包的特性,变量n就能访问到


我的理解,nAdd是全局的,而且又在f1的内部,因此可以访问f1内部的n。

f2在f1的内部,外部无法访问,但是return后result就执行f2的任务。

nAdd执行,n增大1,n既是nAdd访问f1中的n,result()即alert 1000.


因为 n 还在被引用啊,还是有用的东西,怎么能被清除呢?

假如 n 被当作垃圾回收了,你下次再调用 nAddresult 的时候,你怎么知道 n 代表的是哪个值呢?


函数的执行顺序是

function f1(){
    var n=999;
    function f2(){
      alert(n);
    }
    nAdd=function(){n+=1}
    return f2;
  }
  var result=f1();
  result(); // 999
  nAdd();
  result(); // 1000

给 @kikong 的回答赞一个。本题有两个函数都构成了闭包,一个是nAdd作为全局变量,另一个是f2,不过访问f2需要result = f1();result.f2()这样访问。假设你去掉了f2函数,这个n变量依然是不会销毁的,这个你可以通过在nAdd中输出n试试。
闭包其实就是函数内部有函数,且这个里面的函数使用了外部函数的变量。这个时候,变量的生命期就变长了,貌似这样就实现了C语言中的static的作用。要销毁这个变量,就要在使用完后result = null,解除引用,然后JS的内存管理器闲了就会把其销毁了。当然,你这道题,貌似销毁不了,因为nAdd是作为全局变量访问n的。
以上回答是我个人理解的,可能不对。仅供参考!

【热门文章】
【热门文章】