首页 > 用普通人能听懂的话回答什么是闭包

用普通人能听懂的话回答什么是闭包

我一般都是说,创建一个动态存储空间,突破作用域的限制。让外部可以访问内部的变量,感觉自己答得没什么问题啊,为什么面试完没通知?


账号里面的钱就是闭包,这是我能想到的对普通人的解释了...除了账号拥有者(调用者)和银行(内存控制器)之外,谁也不知道里面到底有没有钱,虽然账号相关的东西是暴露的(比如银行卡,但存折不符合要求)


先给出一个理解:闭包是一个函数,保存了自己创建时的环境(一般指父函数中的变量。)
代码示例:

function foo(){
    var a = 1;
    return fn(){
        alert(a);       
    }
}

这里fn就是闭包。
理解前半句后,来看后半句如何理解。

function foo(){
    var date = new Date();
    //获取当前系统时间的秒数
    var a = date.getSeconds();
    return function fn(){
            console.log(a);       
    }
}
var f = foo();
f();

在浏览器中执行后,打开控制台;控制台中的数就是当前系统时间的秒数。(刷新就会变)

这里的将闭包fn赋给了f,当f执行的时候;fn函数就会被创建,fn函数保存了此时的父环境(即a变量的值)。当再次刷新,fn函数创建的时间变了;a的值也就变了。

看到这里应该就知道什么是闭包了,以下是一个闭包的应用;没兴趣的可不看

<div>
  <ol id="clotest" start = "0">
    <li class="item"></li>
    <li class="item"></li>
    <li class="item"></li>
    <li class="item"></li>
  </ol>
</div>
<script type="text/javascript">
    var clotest = document.getElementById('clotest');
    var nodes = clotest.getElementsByTagName('li');
    for(var i = 0; i < nodes.length; i++){
        nodes[i].onclick = function(){
            alert(i);
        }
    }
</script>

这里的需求是点击到哪个列表项;就会出现哪个值。如点击0号列表,就会弹出0。

但是当你实际点击的时候,发现弹出的值一直是4。为什么会出现这种结果呢?
这里定义一组有序列表;点击到哪个列表项;就会出现哪个值。如点击0号列表,就会弹出0。
但是当你实际点击的时候,发现弹出的值一直是4。为什么会出现这种结果呢?

因为闭包保存的是它被创建时父函数中的变量啊!

计算机读取代码的时候,首先for循环;会给nodes[0]、nodes[1]等四个节点的onclick属性赋一个匿名函数。
当你点击某个列表项的时候,匿名函数正式诞生;此时匿名函数的父函数i的值是4,所以当你点击任何一个列表项的时候;弹出的都是4。
那怎么来解决这个问题?

我们需要在每一个node节点绑定的时候,保存下当前i的值。故我们可以定义一个handler的函数,将i作为参数传给handler函数。

var clotest = document.getElementById('clotest');
var nodes = clotest.getElementsByTagName('li');
for(var i = 0; i < nodes.length; i++){
    nodes[i].onclick = handler(i);
}
function handler(i){
    return function(){
        alert(i);
    }
}

这就是闭包的第一个作用,保存现场。


可调用对象(callable object)。


前不久我面试也遇到过这个问题

闭包是指有权限访问另一个作用域中变量的函数。
一般,函数在执行完后会立马释放内存。但是,有时候我们需要在希望函数每次执行“记住”上一次的执行结果,而不是立即销毁。通俗的说,闭包一般用来实现延长记忆力


就是引用了外部变量的内部函数。

顺便贴下自己以前写的文章
http://scarletsky.github.io/2015/12/02/the-little-javascript-closures/


这回答逼格这么高,没收到通知肯定是面试的单位效率不够高


函数内部变量常驻内存


函数背着自己的运行环境,像是一只蜗牛


你爸生了你这个儿子,你有可以自己赚钱,也可以使用你爸的钱,但是,你不想让你爸使用你的钱。

映射到js中。
A函数嵌套B函数,B函数中的变量只能在B函数所产生的作用域中使用,A函数无法使用B函数的变量。但是B可以使用A,和全局作用域的变量。


其实闭包没有那么高深复杂。

仅从规范的角度:

function a() {
    var x = 1, y = 2;
    return function b() {
        console.log(x + y);
    }
}

var f = a();
f();
  1. 在执行a期间,我们可以认为它的局部变量存放在一个内部对象Env里面,a的执行环境保存了对Env的引用。

  2. 在定义b的时候,b也会保存对Env的引用。

  3. a返回时,a的执行环境被销毁,它对Env的引用也就没有了。但是,只要b还可以访问(比如,a通过返回值返回了b)。那么bEnv的引用仍然存在,它仍然可以访问Env中保存的局部变量。

  4. 将来在函数b执行期间,可以通过所谓的作用域链访问到xy

这就是闭包

用图表示就是:


闭包就是泛函。

R语言不服JS。

a <-function () {
     x = 1
     y = 2
     b <- function(){print(x + y)}
    return(b) 
}

a()()

>[1] 3

网上看到的:
闭包其实就是函数的嵌套,内层的函数可以使用外层函数的所有变量,即使外层函数已经执行完毕.

function checkClosure() { //外层函数
    var str = 'China'; //局部变量
    setTimeout(
        function() { console.log(str); } //这是一个匿名函数,可以调用外层函数的变量str
    , 2000);
}
checkClosure();
console.log('Hello'); //先显示Hello,2秒后显示China.

function里用var声明的局部变量在函数执行完就释放了,function外的声明的是全局变量,会一直常驻内存.checkClosure函数的执行是瞬间的,在checkClosure的函数体内创建了一个局部变量str,checkClosure执行完毕之后str并没有被释放,因为setTimeout内的匿名函数存在这对str的引用.待到2秒后函数体内的匿名函数被执行完毕,str才被释放.

又比如PHP在请求结束时,关闭数据库连接:

$db = new mysqli('127.0.0.1','user','pass','dbname',3306);
register_shutdown_function(function() use ($db) {
    $db->close();
});

其中数据库连接$db是匿名函数function()外的变量,PHP跟JS不同之处在于PHP需要使用use显式声明把变量$db传入匿名函数中.


一个函数访问了它的外部变量就是闭包

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