你不知道的JavaScript——闭包

简介

这是《你不知道的JavaScript》系列的第二个比较重要的内容,闭包。在没有仔细研究过闭包的机制前,我对他的了解可能仅仅是函数中返回一个函数,且该函数引用了上一个函数的成员变量。下面我就按我对书中知识的理解做一下总结

闭包

相信了解闭包的同学都是通过下面这个函数来认识闭包的:

function add () {
	var count = 0
	return function fn() {
		count++
		console.log(count)
	}
}

var a = add()
a() // 1
a() // 2

上面这个函数实现的其实就是一个累加器的功能。

闭包实现的原理实际是跟JavaScript的词法作用域有关的,我们仔细观察 add 函数的词法作用域
在这里插入图片描述
在这里面,fn内部引用了父级作用域的count属性,并且在函数中最终返回了 fn 函数。这会造成什么影响呢?

作用域缓存

一般而言,当一个函数在执行的时候会经历这么几个过程:

  • 开辟内存空间来放置执行代码
  • 执行代码
  • 执行完成,销毁对应的内存空间

所以当函数执行完成后,其在内存中开辟的空间会销毁,也就无法在访问其作用域中的成员变量

闭包中的作用域

但是它不走寻常路,在函数执行过程中,fn 是可以调用 add 作用域中的 count 成员(作用域链)。fn 不仅内部调用了 count,而且在 add 外部还被一个 a 变量接收了。这时候,浏览器的回收机制会认为:“哎?fn 还可能会使用 count,还不能回收啊”。这就会导致 add 函数执行时候生成的作用域没办法被销毁,而是一直存在内存中等待 fn 调用或者 fn 释放。
在这里插入图片描述
我们再来看看下面的代码:

var a = null
function add () {
	var count = 0
	a = function fn() {
		count++
		console.log(count)
	}
}

a()

你认为上面的代码能不能形成闭包呢?

答案是:可以的

why?

我们仔细的研究 add 的词法作用域和执行时候的作用域就会发现

  • add 作用域内部的 fn 函数同样引用了 count
  • fn 同样被外部作用域的 a 所引用(接收)

这导致 add 作用域无法有效的释放而形成闭包。

误区

有人可能会想问,那我用外部变量来引用 count 能不能实现闭包呢?(我也这么想过)

// 情况1
var a = null
function add () {
	var count = 0
	a = count
}

// or
// 情况2
var a = null
function add () {
	var data = {}
	return data
}

a = add()

答案是:不可以

首先我们要明确,fn 引用的实际是 count,而不仅仅是 count 的值。也就是说 fn 实际引用的是 count 在内存中的地址。而在 情况1 和 情况2 中,a 对 count 或 data 的引用都只是引用对应的值,而不是它本身。

总结

总的来说,当函数中的某些成员变量被内部某些函数调用,并且该函数被外部其他变量引用(或者说该函数可以在别处调用时)就会形成闭包。

如何消除闭包

在闭包中很关键的一点是:函数内部的函数可以在别处调用,所以我们可以从这个地方入手

function add () {
	var count = 0
	return function fn() {
		count++
		console.log(count)
	}
}

var a = add() // 产生了闭包
a() // 1
a() // 2
a = null // 取消 a 与 fn 的联系,这个时候浏览器回收机制就能回收闭包空间

只要我们将 fn 与外部成员变量的引用关系取消,浏览器就能帮助我们回收对应的内存空间。

模块化

闭包的一个重要的使用就是模块化,它可以将成员变量的命名私有化,不会影响全局

function module() {
	let name = '模块化'
	
	function change(n) {
		name = n
	}
	
	function getName() {
		console.log(name)
	}

	return {
		change,
		getName
	}
}

var module1 = module()
// 我们可以通过模块化访问私有变量 name,并通过对应的 api 方法来获取或者修改这个值
module1.getName() // '模块化'
module1.change('modules')

关于模块化,有许多改进或者优化的地方,这里不做更多的阐述,如果有机会再来讨论他的细节!

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值