深入理解闭包

举报
Trista2222 发表于 2020/11/10 16:34:42 2020/11/10
【摘要】 先不说闭包是什么,我们先理解js作用域的概念,作用域,简单说就是定义一个变量,我们能在哪些范围内使用它,var a = 1;function fn() { var b = 2; console.log(a); console.log(b);}fn(); // 1 2上面是一个简单的例子,a是全局变量,b是函数fn作用域内的变量,js是链式作用域,当使用一个变...

先不说闭包是什么,我们先理解js作用域的概念,作用域,简单说就是定义一个变量,我们能在哪些范围内使用它,

var a = 1;
function fn() {
    var b = 2;
    console.log(a);
    console.log(b);
}
fn();        // 1  2

上面是一个简单的例子,a是全局变量,b是函数fn作用域内的变量,js是链式作用域,当使用一个变量时,会沿着作用域向上寻找该变量。所以能在函数内部能打印出a、b的值。反之,

var a = 1;
function fn() {
    var b = 2;
}
console.log(a);    // 1
console.log(b);    // 报错 b is not defined

在全局作用域无法访问fn作用域内的变量。

闭包是怎么一种存在呢,它打破了这种“函数外部无法访问函数内部变量”的约束,来看看它是如何做到的

function foo() {
    var name = "Eva";
    function bar() {
        console.log(name);
    }
    
    return bar;
}

var result = foo();
result();    // Eva

在foo函数内定义了一个函数bar,并且在bar中console.log了foo中的变量name,并将bar返回,在全局执行result,打印出了name的值,也就是说我们上面无法实现的在全局作用域无法访问函数内部变量的问题是不是被解决了??没错!!!

为什么呢?因为通常一个函数执行完后,就会被垃圾回收机制回收,那它的作用域也咩有了,但是这个例子中在将bar返回的时候,foo函数作用域作为它的父作用域被保存下来了,并没有被回收,所以依然能访问到foo的作用域。这也就是它为什么会内存泄露的原因,如果不手动释放,它会一直存在着。


我们再看几个做题时经常碰到的题目,

for (var i = 0; i < 5; i++) {
    setTimeout( function timer() {
        console.log(i);  
    }, 1000);
}
// 5 5 5 5 5

结果是5个5,因为所有的console语句的i变量都是从for循环这个作用域取的,而1秒之后,这个作用域的i已经是5了,所以会打印出5个5.

for (var i = 1; i <= 5; i++) {
    (function(i){
        setTimeout( function () {
              console.log(i);
          },  1000 );
    })(i);
 }
 // 1 2 3 4 5

上面的这段代码,for循序里定义了一个立即执行函数,这种函数一般用于构造私有变量,避免全局污染,每个console语句都有自己的作用域,且作用域中保存了当前的i的值,所以在1000ms后执行这些console语句,都是从各自作用域获取的i。

我们再看一个例子,

for (let i = 1; i <= 5; i++) {
      setTimeout( function timer() {
               console.log(i);  
      }, 1000 );
}
// 1 2 3 4 5

这段代码使用了let来定义变量,我们都知道let定义的变量的块级作用域,将i值绑定到循环的每次迭代中,也就是每个console语句上,也很好的解决了问题。


总结:在《js高级程序设计》中,将闭包定义为有权访问另一个函数作用域中的变量的函数。但是我更倾向于另一种说法:引用了自由变量的函数。函数中定义一个函数来访问父函数变量并返回该函数只是闭包的实现方式。而返回的才是闭包,携带自由变量作用域的一个函数。

【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息, 否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。