JS之闭包

JavaScript之闭包

使用闭包主要是为了设计私有的方法和变量

闭包的优点是可以避免全局变量的污染,缺点是闭包会常驻内存,增加内存使用量,使用不当很容易造成内存泄漏,在JavaScript中。函数即闭包,只有函数才会产生作用域

闭包有三个特性

(1)函数嵌套函数

(2)在函数内部可以引用外部的参数和变量

(3)参数和变量不会被垃圾机制回收

闭包的其他理解

闭包虽不是JS的特色功能, 但要理解还真要费那么点工夫.

在理解闭包之前, 首先要清楚JS中的作用域只有2种: 全局作用域方法作用域

全局作用域很好理解了, 方法作用域就是指一个 function 形成一个独立的作用域, 而且方法作用域还能够嵌套.

与别的语言不同的是: 花括号({})不能形成一个独立的作用域, 例如Java中的作用域.

下面我们举例说说作用域

var g = 0;
function f() {
    // 这里面就形成了一个方法作用域, 能够保护其中的变量不能被外部访问
    // 方法作用域能够访问全局作用域
    var a = 1;
    console.log(g);
 
    // 嵌套方法作用域
    function ff() {
        // 这里面再度形成了一个方法作用域
        // 其中可以访问外部的那个方法作用域
        var aa = 2;
        console.log(a);
    }
 
    // 出了 ff 的作用域就不能访问其中的东西了
    // console.log(aa); // 报错 ReferenceError: aa is not defined
}
f();
// console.log(a); // 报错 ReferenceError: a is not defined

弄清楚作用域的问题后, 我们就可以开始聊聊闭包了.

我们以最经典的for循环为例来讲解. 大家可以试试下面这段代码, 取自JavaScript 秘密花园循环中的闭包

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

如果在运行前, 你没有猜到正确答案, 那就对了…

1.首先说说为什么最终输出的是10次10, 而不是你想象中的 0, 1, 2, 3, 4, 5, 6, 7, 8, 9

因为setTimeout是异步的!
你可以想象由于setTimeout是异步的, 因此我们将这个for循环拆成2个部分
第一个部分专门处理 i 值的变化, 第二个部分专门来做setTimeout
因此我们可以得到如下代码

 // 第一个部分
 i++;
 ...
 i++; // 总共做10次

 // 第二个部分
 setTimeout(function() {
    console.log(i);
 }, 1000);
 ...
 setTimeout(function() {
    console.log(i);
 }, 1000); // 总共做10次

这样一拆后, 我相信你肯定知道之前那个for循环的运行结果了.

由于循环中的变量 i 一直在变, 最终会变成10, 而循环每每执行setTimeout时, 其中的方法还没有真正运行, 等真正到时间执行时, i 的值已经变成 10 了!

i 变化的整个过程是瞬间完成的, 总之比你异步要快, 就算你setTimout是0毫秒也一样, 会先于你执行完成.

for(var i = 0; i < 10; i++) {
    setTimeout(function() {
        console.log(i);
    }, 0);
}

2.那么为什么setTimeout中匿名function没有形成闭包呢?

因为setTimeout中的匿名function没有将 i 作为参数传入来固定这个变量的值, 让其保留下来, 而是直接引用了外部作用域中的 i, 因此 i 变化时, 也影响到了匿名function.

因此如果我们定义一个外部函数, 让 i 作为参数传入即可"闭包"我们要的变量了!!

   for (var i = 0; i < 10; i++) {
       // 注意关键是我们把想要闭包的值当参数传入一个方法
       // 这个方法 return 一个新的方法 -- 闭包!!
       setTimeout(fn(i), 1000);
   }
   
   function fn() { // 为了深刻理解闭包, 这个函数我没有用参数
       // 神奇的"闭包"发生在这一步, 其实就是作用域和值复制在起了关键作用,
       // 对于数字/字符等类型是复制值, 而不是引用
       var a = arguments[0];
       return function() {
           console.log(a); // 注意现在我操作的变量已经变成 a 了,
                                     // 已经和 i 没有半毛线关系了!
                                     // 而 a 的值就是当时执行时赋予的一个确定值,
                                     // 不会因 i 的变化而变化了!
       };
   }

3.再换成更简洁的方式看你能不能真正理解闭包

   for (var i = 0; i < 10; i++) {
       (function(a) {
           // 变量 i 的值在传递到这个作用域时被复制给了 a,
           // 因此这个值就不会随外部变量而变化了
           setTimeout(function() {
               console.log(a);
           }, 1000);
       })(i); // 我们在这里传入参数来"闭包"变量
   }
 
//这就是我所理解的闭包, 简单点说就是专门用来"包养"变量的.
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值