老生常谈的JavaScript闭包
老生常谈的闭包
很多观点参考于《你不知道的JavaScript》、《JavaScript忍者秘籍》,私信我,可发电子书呀。进入正文:
也许你并不知道闭包是什么,但是你的代码中到处都有闭包的影子!也许你觉得闭包平时用不到,但是每次面试你都得去准备这个方面内容!也许你不觉得这个功能有什么用,但是很多框架的功能都是基于闭包去实现的!
下面我们将目光聚焦到以下几个问题,来理解一下闭包:
- 词法作用域
- 闭包的形成
- 闭包的概念
- 闭包的常见形式
- 闭包的作用
闭包与词法作用域
在《你不知道的JavaScript》书有一句原话:闭包是基于词法作用域书写代码所产生的自然结果。所以在知道闭包之前,得先理解什么是词法作用域。以前的文章有过介绍: 理解JavaScript的词法作用域(可以稍微的翻看一下)。如果不想看,也没关系。 接下来我们分析一段代码,去理解什么是词法作用域,以及闭包的形成。
var a = 100
function foo() {
var a = 10
function test() {
var b = 9
console.log(a + b)
}
return test
}
var func = foo()
func()
作用域分析
上图我们清晰的反应了作用域嵌套。
- 其中全局变量func就是test函数的引用。
- test定义虽然定义在foo包裹的作用域内,但运行在全局作用域内。
- test里面执行a + b的时候,a变量的值等于10而不是等于100。说明变量a的查找跟test在哪里执行没有关系。
- 这就是词法作用域,在书写阶段作用域嵌套就已经确定好了,跟函数在哪里运行没有关系。
闭包的形成
对上诉代码进行作用域分析之后我们不难得出一个结论:test函数不管在哪里执行,他永远都属于foo作用域下得一个标识符,所以test永远对foo作用域持有访问的权限。
正常情况下,foo函数执行完毕后,js的垃圾回收机制就会对foo函数作用域进行销毁。但是由于test函数对foo的作用域持有引用,所以只要程序还在运行中,你就永远不会知道test会在哪里被调用。 每当test要执行的时候,都会去访问foo作用域下的a变量。所以垃圾回收机制在foo执行完毕之后,不会对foo作用域进行销毁。这就形成了闭包
闭包的常见形式
以上我们分析了,闭包是怎么形成的。也分析了一段典型的闭包代码。前言中我们有说过一句话也许你并不知道闭包是什么,但是你的代码中到处都有闭包的影子。接下来我们分析一下,闭包的常见形式。
以下代码就算你不了解闭包,你也写过。类似的:
computed: {
add() {
return function(num) {
return num + 1
}
}
},
vue中可接受参数的计算属性
function init() {
$(.name).click(function handleClickBtn() {
alert('click btn')
})
}
初始化函数中使用jq绑定事件
$.ajax({url:"/api/getName",success:function(result){
console.log(result)
}});
ajax请求数据
window.addEventListener('click', function() {
})
原生注册事件
可以发现当把函数当做值传递的时候,就会形成闭包。《你不知道的JavaScript》给出了总结: 如果将函数(访问它们各自的词法作用域)当作第一
级的值类型并到处传递,你就会看到闭包在这些函数中的应用。在定时器、事件监听器、
Ajax 请求、跨窗口通信、Web Workers 或者任何其他的异步(或者同步)任务中,只要使
用了回调函数,实际上就是在使用闭包!
闭包的作用
闭包的一大用处是回调函数。还有一个作用是封装私变量。在《JavaScript忍者秘籍》有专门章节对其进行详细讲解。 下面我们看看闭包如何封装私有变量
私有变量封装
场景:有一个函数foo, 统计其被调用的次数
var num = 0
function foo() {
// 次数加一
num = num + 1
return num
}
foo()
foo()
foo()
console.log(num)
全局变量num来统计foo调用次数,最大的坏处在于,你不知道程序运行到什么时候,num的值被篡改。如果能够将num变量私有化,外部不能随意更改就好了。
function baz() {
var num = 0
return function() {
num++
return num
}
}
var foo = baz()
foo()
foo()
let fooNum = foo()
console.log(fooNum)
通过闭包,num被私有化在baz作用域下,程序运行过程中,不能随意更改baz下的num值。
小结
- 闭包是基于词法作用域书写代码产生的自然结果
- 闭包经常出现在我们的代码里面,常见的是回调函数
- 闭包作用很多,回调函数,私有化变量等等
作者:limbo
链接:https://juejin.cn/post/6989148728649072653