网站首页 > 教程文章 正文
结合 MDN 官网中 JavaScript 章节中对闭包详解,我们需要理清的问题有,什么是闭包、闭包产生的条件、以及闭包的用途。
一、什么是闭包?
1.1 闭包的概念
闭包(closure)是一个函数以及其捆绑的周边环境状态(lexical environment,词法环境)的引用的组合。换而言之,闭包就是让开发者可以从内部函数访问外部函数的作用域。在 JavaScript 代码环境中,闭包会随着函数的创建而被同时创建。
1.2 初步理解闭包的定义
当函数可以记住并访问所在的词法作用域时,闭包就产生了,即使函数是在当前词法作用域之外被执行。根据上面的理解,让我们实际操作一下,先看以下代码:
function globalFun () {
let a = 0;
return function localFun () {
console.log(a);
};
};
const resolve = globalFun();
resolve(); // 控制台打印 0
这是一段非常经典的闭包知识相关的代码,通过以上代码我们可以理解到闭包的产生会有三个条件:
1.globalFun函数内返回localFun函数。
2.localFun函数调用 (使用) 了globalFun函数的局部变量。
3. localFun 函数被调用。
1.3 小结
我们在外部环境访问了内部环境的变量,并且正常执行了globalFun的作用域会被销毁掉,垃圾回收机制也会释放掉内存空间。显然闭包阻止了这件事情的发生。
二、闭包产生的条件有哪些?
由于localFun声明的位置,使其拥有涵盖了globalFun的内部作用域,当localFun被调用后,会保持对globalFun作用域的引用,访问a变量。保持对外部作用域变量的引用,就会产生闭包,而不是非要返回函数。
function waiting(msg) {
setTimeout(function () {
console.log(msg)
}, 1000)
}
waiting('js')
不管做了什么操作,只要你将内部函数传递到所在的词法作用域之外,他都会保持对内部作用域的引用,无论在哪里执行内部函数,都会创建对应的闭包。例如这个例子中setTimeout保持对waiting的引用,在执行时也会创建闭包。
三、闭包的用途?
当我们重复使用一个变量名时,会考虑到命名污染的问题,这时候就可以使用闭包。这种变量被叫做私有变量或者局部变量。
for(var i = 0; i <= 10; i++){
setTimeout(function (){
console.log(i)
},i)
}
每次循环,我们都会挑出一份i用来输出,但因为setTimeout会在循环完成后执行,每次的i都在同一全局作用域下,于是后来居上,覆盖了前面的i,再由setTimeout执行时,就全是11了。
怎么使得每次循环输出正确呢?
我们只需要将每次的i变成一个私有变量,有独立的作用域,让其不再篡改就OK了。
这个时候我们就需要使用到IIFE( 立即调用函数表达式),如下是来自MDN 官网对IIFE的解释:
(function () {
statements
})();
这是一个被称为 自执行匿名函数 的设计模式,主要包含两部分。第一部分是包围在 圆括号运算符 () 里的一个匿名函数,这个匿名函数拥有独立的词法作用域。这不仅避免了外界访问此 IIFE 中的变量,而且又不会污染全局作用域。
第二部分再一次使用 () 创建了一个立即执行函数表达式,JavaScript 引擎到此将直接执行函数。
for(var i = 1; i <= 5; i++) {
(function (j) {
setTimeout(function () {
console.log(j)
}, j* 1000)
})(i);
}
这里利用IIFE拥有独立的词法作用域的特性,将变量私有化,这样在setTimeout执行时就会得到正确输出。没错,好像就是利用闭包将每次的变量缓存起来,放在独立的内存中。
四、总结
闭包就是一个函数引用另外一个函数的变量,因为变量被引用着所以不会被回收,因此可以用来封装一个私有变量。这是优点也是缺点,不必要的闭包只会徒增内存消耗!另外使用闭包也要注意变量的值是否符合你的要求,因为他就像一个静态私有变量一样。闭包通常会跟很多东西混搭起来,接触多了才能加深理解。
- 上一篇: 前端开发79条知识点汇总(前端开发知识点总结)
- 下一篇: 抖音品质建设 - iOS启动优化《实战篇》
猜你喜欢
- 2025-07-27 8个前端面试的题目(前端面试题2020及答案 知乎)
- 2025-07-27 深入理解Node.js中的垃圾回收和内存泄漏的捕获
- 2025-07-27 网易+腾讯+阿里,前端工程师面经!30K果然不是很好拿
- 2025-07-27 go errgroup 获取gorouting错误信息
- 2025-07-27 盛趣游戏unity客户端面试(盛趣游戏招聘岗位)
- 2025-07-27 Swift 性能探索和优化分析(swift运行效率)
- 2025-07-27 「前端开发」eval() 函数认知和学习以及注意事项
- 2025-07-27 解锁C++灵魂:函数指针场景及实例(c++函数指针和指针函数)
- 2025-07-27 2021 年 Node.js 开发人员学习路线图
- 2025-07-27 跨越十年的C++演进:C++11新特性全解析
- 最近发表
- 标签列表
-
- location.href (44)
- document.ready (36)
- git checkout -b (34)
- 跃点数 (35)
- 阿里云镜像地址 (33)
- qt qmessagebox (36)
- mybatis plus page (35)
- vue @scroll (38)
- 堆栈区别 (33)
- 什么是容器 (33)
- sha1 md5 (33)
- navicat导出数据 (34)
- 阿里云acp考试 (33)
- 阿里云 nacos (34)
- redhat官网下载镜像 (36)
- srs服务器 (33)
- pico开发者 (33)
- https的端口号 (34)
- vscode更改主题 (35)
- 阿里云资源池 (34)
- os.path.join (33)
- redis aof rdb 区别 (33)
- 302跳转 (33)
- http method (35)
- js array splice (33)