js闭包通俗讲解

一、从使用角度谈闭包

满足以下列条件的就是闭包:

1.定义一个函数(outer),该函数存在声明的局部变量(b)

2.该函数的返回值也是一个函数(inner)

3.返回的函数(inner)调用了该函数声明的局部变量(b)

4.该函数被调用(outer)

function outer() {
   
    var b = 1;
   
    function inner() {
      console.log(b);
    }

    return inner;
}

// 该函数被调用
outer()

二、从作用域角度谈闭包

闭包实际就是一种能使局部变量不被销毁的特殊写法。

在ES5时代,变量只有2种:全局变量和函数产生的局部变量。

全局变量可以被任意引用的外部文件、环境、代码覆盖,并且不会有任何提示。如果我们想要一个不会被随意修改的变量、只能由特定方式触发变化的安全变量,只能考虑函数的局部变量。

函数内的局部变量不能被外部任意访问,只能在该函数内被引用,这似乎是个极好的、使变量保持纯净的方法。

但我们知道,函数在被调用时才会加载至内存,调用一旦结束就会被销毁,所以一般函数产生的局部变量调用结束后就不存在了。

如果想要函数的局部变量一直存在,那就需要该函数内的变量因被调用而无法销毁。

为了达到这个目的,我们的写法需要如上述闭包用法所要求的那样。

下面有一个例子:

function xm_bank() {
  var money = 3000;
  function buy(count){
    money -=count;
    console.log('小明买了'+count +'元的东西后还有' + money+ '元');
  }
  return buy;
}

var xmBank = xm_bank();
xmBank(30); // 小明买了30元的东西后还有270元
xmBank(70); // 小明买了70元的东西后还有2900元
xmBank(1000); // VM306:5 小明买了1000元的东西后还有1900元

通过闭包,我们实现了对局部变量money的保存。

三、从内存角度谈闭包

JavaScript是解释型语言,逐行解释执行,但在执行之前,还需要经过编译阶段。

编译阶段产生执行上下文,执行上下文主要包括变量环境、词法环境、outer、this。

变量环境是一个栈结构。函数被调用时产生函数执行上下文,函数内定义的变量仅在该函数上下文环境中使用。函数调用结束后该函数上下文环境销毁。

闭包的不同在于,在销毁函数上下文时,发现该函数内变量被返回的函数调用,因为将产生一个函数名(closure)的对象,用来存放该局部变量。

在 JavaScript 中,根据词法作用域的规则,内部函数总是可以访问其外部函数中声明的变量,当通过调用一个外部函数返回一个内部函数后,即使该外部函数已经执行结束了,但是内部函数引用外部函数的变量依然保存在内存中,我们就把这些变量的集合称为闭包。比如外部函数是 foo,那么这些变量的集合就称为 foo 函数的闭包。

当执行到 bar.setName 方法中的myName = "极客邦"这句代码时,JavaScript 引擎会沿着“当前执行上下文–>foo 函数闭包–> 全局执行上下文”的顺序来查找 myName 变量:

由于没有“人”知道foo(closure)内的变量什么时候会被使用,所以V8垃圾回收机制无法自动回收。。

我们也可以接助Chrome 的“开发者工具”看下:

刚才我们说了,函数产生的闭包对象不会被V8引擎自动收集,如果引用闭包的函数是个全局变量,那么直至页面销毁才会被回收;如果引用闭包的函数是个局部变量,判断闭包这块内容如果已经不再被使用了,那么 JavaScript 引擎的垃圾回收器就会回收这块内存。

这就是为什么我们说,闭包会造成内存泄漏的原因。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值