JavaScript进阶讲解二—>闭包

上一节我们讲了作用域

大家需要记住一句话:函数的作用域与他定义的位置有关,与调用位置无关。
今天让我们来学习闭包吧


一、JS的内存管理

  1. JS对于基本数据类型内存的分配,会在执行时,直接在栈空间进行分配。
  2. JS对于复杂数据类型内存的分配,会在堆内存中开辟一块空间,并且将这块空间的指针返回值变量引用;
    在这里插入图片描述

二、JS的垃圾回收(Garbage Collection->GC)

js中时有垃圾回收机制的(当然像c和c++就没得,这里不多赘述),因为内存的大小是有限的,当内存不再需要的时候,我们就需要对其进行释放。像那些不再使用的对象,我们都称之为是垃圾,它就会被回收。

2.1常见GC算法

  1. 引用计数。像我们上面的图那样,堆结构中的obj是有个箭头指向他的,所以他就是被引用的,有一个引用,计算器就+1(会存在循环引用的问题),如果你想手动清除,可以把obj=null
  2. 标记清除(JS引擎比较广泛的采用的就是标记清除算法,不会存在循环引用的问题),如下图的m,n就是会被清除的 ,根就是GO
    在这里插入图片描述

三、闭包

为什么我们在讲闭包前,还要讲js的内存,因为闭包里是可能会有内存泄漏的问题,所以先给大家说了下js的内存是如何管理的。
在js中函数是非常重要的,并且是一等公民,那么函数的使用就是非常灵活的,可以作为另外一个函数的参数,也可以作为另外一个函数的返回值来使用。

3.JS中闭包的定义

维基百科的解释:

  1. 闭包(英语:Closure),又称词法闭包(Lexical Closure)或函数闭包(function closures)。
  2. 是在支持 头等函数 的编程语言中,实现词法绑定的一种技术。
  3. 闭包在实现上是一个结构体,它存储了一个函数和一个关联的环境(相当于一个符号查找表)。
  4. 闭包跟函数最大的区别在于,当捕捉闭包的时候,它的 自由变量 会在捕捉时被确定,这样即使脱离了捕捉时的上下文,它也能照常运行;
    MDN的解释:
  5. 一个函数和对其周围状态(lexical environment,词法环境)的引用捆绑在一起(或者说函数被引用包围),这样的组合就是闭包(closure)。
  6. 也就是说,闭包让你可以在一个内层函数中访问到其外层函数的作用域。
  7. 在 JavaScript 中,每当创建一个函数,闭包就会在函数创建的同时被创建出来。

3.1标准闭包

function foo() {
  var name = 'foo'
  function bar() {
     console.log(111, name);
  }
  return bar
}
var fn = foo()
fn()

看上面的代码就是严格意义上的闭包,因为按照正常来讲,当var = foo() 执行完时,意味着foo中的var name = “foo”是应该被销毁的,但是很明显在执行fn()时,name是还可以访问的,。也就是说:我们在捕捉bar这个函数时,name这个变量也被一起捕捉了,他两一起就形成了闭包,这也契合了维基百科的解释。

3.2广义的闭包

var a = 1
function foo() {
     console.log(a);
}
foo()

我们来看这段代码,大家觉得这是不是个闭包,我觉按照上面的官方定义这个也是闭包,因为他有函数也可以访问自由变量。

function foo() {
            
}

那我们再看,这个呢?这个的争议就比较大了,因为从可以访问自由变量的角度来说,他当然是可以的,他也是个函数,所以我们也已认为他是闭包。

3.2闭包总结

  1. 闭包由两部分组成:函数+可以访问的自由变量 也就是说:一个普通的函数,如果它可以访问外层作用域的自由变量,那么这个函数就是一个闭包。
  2. 从广义的角度来说:JavaScript中的函数都是闭包。
  3. 从狭义的角度来说:JavaScript中一个函数,如果访问了外层作用域的变量,那么它是一个闭包。

四、代码执行过程

加深下大家对代码执行过程的理解

var message = '111'
function foo() {
  var name = "foo"
}

function bar() {
  console.log("bar")
}

foo()
test()

1.解析
在这里插入图片描述
2.开始执行
var message = ‘111’ 所以GO中的message变成‘111’
接下来是foo() 此时又会开始解析foo
函数的解析会先创建 函数执行上下文,生成AO对象,函数中的VO就是AO
在这里插入图片描述
3.foo解析完成才开始执行foo,执行这里就不画了,这里就是把AO中的name变为‘foo’
4. foo执行完成,销毁函数上下文(此时没有指针指向AO所以AO也销毁了)
在这里插入图片描述
5.开始执行bar()这里就不画了 因为是同样的先解析bar在执行,在销毁。

五、闭包中的内存泄露

定义:该被销毁的AO,却始终都没有被销毁,此时就造成了内存泄露。(为什么没被销毁,肯定是有指针一直指着他,所以无法销毁)
怎么解决呢? 很简单,将其设置为null。
当然要不要将其设置为null,取决于我们还会不会用,如果我们还要用,那就没必要手动销毁它了。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值