面试题:为什么要用闭包?

看了这个回答似乎也不是了解的很透彻啊

我是学Java的。今天在面试的时候面试官提及匿名类,我说Java8里面提供了Lamada式,在JS里也有闭包这个概念。面试官问什么是闭包?为什么要用闭包?

  • 我说:用来控制访问啊。内部可以访问外部,但是外部不能访问内部。

面试官似乎不是很满意这样的回答。不知各位大神对这个问题有什么看法?

阅读 23.7k
13 个回答

闭包,顾名思义,就是把馒头变成包子~

馒头全是面粉,包上馅就成了包子

包子是带馅的馒头

闭包是自带运行环境的函数

发哥是自带背景音乐的男人~


有童鞋不理解“自带运行环境”的含义~

再举例说一次吧~

码农们都吃过方便面吧~
它和普通面条有什么区别呢?
就是 自带调味包 。
调味包就是方便面的烹饪环境。
它简化了煮面条的流程。让用户不必练就厨艺也能吃上美味的内牛满面。

函数式编程的闭包,就是函数的调味包。
方便用户调用函数。不必为了维护繁杂的外部状态而烦恼。
例如python,就把闭包玩出了很多花样:
静态私有变量啦~
偏函数啦~
单参化~
装饰器~
……

当你在用这些功能的时候,其实就是在吃别人设定好调味包的“方便面”。

你的回答是关于作用域的回答,不是关于闭包的。而且这个回答也是属于不严谨的回答。你根本解释不清什么是内部,什么是外部。

我认为闭包是这样的。当一个函数在定义它的作用域以外的地方被调用时,它访问的依然是定义它时的作用域。这种现象称之为闭包。

具体用途有好多,常见的有创建私有属性,函数柯里化等等。

------------分割线------------

我再补充一下,其实闭包的本质是静态作用域。因为 JavaScript 没有动态作用域,所以函数访问的都是定义时的作用域,所以闭包才得以实现。

其他答案里说闭包是自带运行环境的函数。但是实际上,JavaScript 里任何函数不都是自带运行环境的函数吗?有的人也因此认为所有的函数都是闭包。这当然也不算错,但对理解闭包其实意义不大。因为你平时都是这么使用函数的,即使你不知道什么是闭包,也不会出什么问题。只不过平时你可能没有意识到全局作用域就是一个大闭包。

我们常见的闭包形式就是a 函数套 b 函数,然后 a 函数返回 b 函数,这样 b 函数在 a 函数以外的地方执行时,依然能访问 a 函数的作用域。其中“b 函数在 a 函数以外的地方执行时”这一点,才体现了闭包的真正的强大之处。

总之,闭包只是基于静态作用域的一个编程技巧。从面试的角度来说,你要回答什么是闭包,你首先得解释什么静态作用域的特点,然后还必须要强调“b 函数在 a 函数以外的地方执行时”这一点,才算是对闭包的完整回答。

楼主的回答并不准确,如果我是面试官我也不满意。
简单来说,闭包是指当函数被当成对象返回时,如果夹带了外部变量就形成了闭包。我非常赞同那位比喻把馒头加上馅变成包子的同学的回答,他虽是调侃成分居多,但理解的程度非常之深刻。

如果一个函数打包了外部变量,就可以给程序非常大的灵活性,你可以把闭包理解成轻量级的接口封装,虽然对外都是这个函数(调用方式不变),但是因为之中的变量不一样,就可以完成很多功能。这也就是那位同学说的自带运行环境的函数,自带背景音乐的男人,想想都可怕。

如果你想还深入了解一点,可以参考我总结的一篇文章,详解Python中的闭包,虽然编程语言不一样,但是道理是一样的。

所谓“闭包”,指的是一个拥有许多变量和绑定了这些变量的环境的表达式(通常是一个函数),因而这些变量也是该表达式的一部分。
在Javascript中创建一个闭包来解释闭包最好不过:

function a(){
var i=0;
function b(){
alert(++i);
}
return b;
}
var c=a();
c();

函数b嵌套在函数a内部;函数a返回函数b。
这样在执行完var c=a( )后,变量c实际上是指向了函数b,再执行c( )后就会弹出一个窗口显示i的值(第一次为1)。这段代码其实就创建了一个闭包,因为函数a外的变量c引用了函数a内的函数b,也就是说:当函数a的内部函数b被函数a外的一个变量引用的时候,就创建了一个闭包。

闭包和回调是分不开的,当且仅当你使用回调的时候,才可能用到闭包的概念。
所以问题转化为,你为什么要使用回调。
所以问题转化为,回调有哪些特点和应用场景。
其实我个人感觉,回调是偏函数式的一种编程理念。正如JAVA通过对象提供的各种public方法来修改对象内部状态,从而保证内部状态的安全。这是语言层面的。
同样,你可以通过回调函数来改变闭包的内部状态,因此有时候你也可以把闭包看成一个JAVA对象:闭包内的状态都是private,只有回调可以访问它们,回调是这个对象唯一的public方法。
至于为什么要回调,原因就非常多了,什么异步啦,抽象策略啦,curry化(我特别喜欢这个概念)啦,等等。

lambda演算式只允许单输入单输出,所以lambda a, b: a + b就等于lambda a: lambda b: a + b也就是currying。

简单来说,闭包的定义是:函数能访问它被定义时的作用域。

所以,你说的访问控制之类的,只是闭包的一个应用场景而已,当然没有回答什么是闭包的问题,面试官自然就不满意。

另外,Java里面其实是不支持闭包的,匿名内部类看起来跟闭包差不多,但实际上不管是功能性还是实现层面都不能算是闭包。因为:

  1. 从功能上,匿名内部类里面访问的变量必须是final(Java8隐含声明final)

  2. 实现上,匿名内部类里访问的final变量值其实是从外面被拷贝进去了,所以其并不能真正访问到之前的作用域,这也是为什么必须是final的原因。

其实按照闭包的一般写法形式,简单的来说就是 函数里面又嵌套了函数。在团队开发中,为了防止命名冲突,我们一般会把相应的代码用闭包的形式包裹起来,以避免暴露在全局作用域下面。但是有个不好的地方是其内部变量不会被立马回收,有内存溢出的风险。

常驻内存,不会被gc回收,访问更快

到现在我也没搞明白闭包是个啥。

1.

function f() {
    return function(x) {
        return 'hello' + x;
    };
}

2.

function f() {
    var str = 'hello';
    return function(x) {
        return str + x;
    };
}

我知道对于概念非常在意的人一定会讲,第2个是闭包,而第1个只是返回一个匿名函数。

我实在搞不清楚这东西跟匿名/回调函数有啥不一样。这种概念有必要分的这么清楚吗?经常看到有关闭包的讨论,都甚是激烈。

我的理解闭包就是一个匿名函数,只是恰好当时的环境用到了一个变量,延长了该变量的作用范围及存活时间
Anonymous function 维基百科关于匿名函数解释的1.2就是Closures(闭包)

新手上路,请多包涵

java里,简单说就是要调用一个函数(其实也是一个对象),然而这个对象的真正实现在其他地方(别的类或函数中),为什么要写到其他地方呢,因为其他地方有该函数现实的环境(也就是一些变量,对象什么的东东)即一个引用了自由变量的函数(自由变量就是这个环境),如果不用闭包实现的话,自己需要去维护一个可能非常复杂的环境,Lambda表达式可以表示闭包,函数式编程简单说就是把一个逻辑的实现用多个函数嵌套去实现

当一个函数内部嵌套另一个函数定义时,内部的函数体可以访问外部的函数的局部变量,这种特征我们称作词法定界。虽然这看起来很清楚,事实并非如此,词法定界加上第一类函数在编程语言里是一个功能强大的概念,很少语言提供这种支持。 技术上来讲,闭包指值而不是指函数,函数仅仅是闭包的一个原型声明;
闭包在上下文环境中提供很有用的功能,作为函数嵌套的函数(newCounter)。这一机制使得我们可以在 Lua 的函数世界里组合出奇幻的编程技术。闭包也可用在回调函数中,比如在 GUI 环境中你需要创建一系列 button,但用户按下 button 时回调函数被调用,可能不同的按钮被按下时需要处理的任务有点区别。
闭包在完全不同的上下文中也是很有用途的。因为函数被存储在普通的变量内我们可以很方便的重定义或者预定义函数。通常当你需要原始函数有一个新的实现时可以重定义函数。

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题
宣传栏