JavaScript闭包的理解

闭包是指有权访问另一个函数作用域中的变量的函数。

作用域

作用域分为全局作用域局部作用域

全局作用域:最外层函数定义的变量拥有全局作用域,任何内部函数都可以访问。
局部作用域:只在固定的代码片段内可以访问,函数外部无法访问。

全局变量都是全局对象window的一个属性。

作用域链

作用域链就是根据内部函数可以访问外部函数变量的这种机制,链式向上查找决定哪些数据可以被内部函数访问。
当某一个函数被调用时就会创建一个执行环境和相关作用域链

作用域链的用途,是保证有权访问当前执行环境的所有变量和函数的有序访问。

闭包

某些时候我们需要得到一个函数内部的局部变量,这时就需要用到闭包。
闭包是指有权访问另一个函数作用域中的变量的函数。
闭包的创建方式是在一个函数内部创建另一个函数。

function f1(){
   var n=999;
   function f2(){
       console.log(n);   //n=999;
   }
}

上面的函数中,f2被包含在f1内部,f2可以得到f1中的所有局部变量,但f1无法得到f2中的局部变量。
那么只要把f2作为返回值,就可以被f1获取。

function f1(){
   var n=999;
   function f2(){
       console.log(n);   //n=999;
   }
   return f2;
}
var result=f1(); //返回的是f2函数
result(); //999

这个f2函数就是闭包。

闭包的作用

闭包可以用来读取内部函数的变量,还可以让这些变量的值始终保存在内存中,不会在f1调用后被自动清除。

原因是f1是f2的父函数,f2被赋予了一个全局变量,所以f2始终在内存中。而f2的存在依赖于f1,所以f1也始终存在于内存中,不会在调用结束后被垃圾回收机制回收。

注意

注意在函数内部声明变量时要用var,否则声明的是一个全局变量。

闭包会使函数中的变量都被保存在内存中,消耗较大,所以不能滥用会造成网页性能问题。IE中可能导致内存泄露。解决方法:退出函数之前将不使用的局部变量全部删除。

面试题

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

为什么会输出5个数字6?

因为setTimeout()函数是一个放在任务队列中的异步任务,要等执行完栈中的同步任务代码,然后开始执行这个定时器定时器。所以在定时器的方法执行的时候,变量i已经变成了6,所以输出的全部是6。

那么怎么样才能输出1、2、3、4、5呢?

因为5个定时器所打印出来的是同一个i变量,所以想要实现输出不同的数字,就需要把每个定时器所访问的变量独立起来,这就用到了JavaScript的闭包。闭包用途很多,可以很好地区分开各个作用域,避免变量的混淆,但是滥用闭包也会导致性能问题。

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

块级作用域–关键字let

使用闭包可以得到正确的结果,原因就是改变了i的作用域,那如果我们把循环中的每个setTimeout都独立成一个作用域是不是也能实现同样的结果呢?我们都知道,在JavaScript中,每个函数是一个独立的作用域,但是“{}”是不能形成独立作用域的。

在ES6中提出了一个新的关键字let,就可以声明一个仅对当前“{}”内部有作用的变量。输出的结果是一样。

for (let i = 1; i <= 5; i++) {
  setTimeout( function timer() {    
      console.log(i);    
  }, 1000 );    
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值