网站首页 > 技术文章 正文
前言
在JavaScript中,闭包是一个非常重要的概念,对于编写高质量的JavaScript代码和理解某些设计模式非常关键。很多人认为闭包很难,不过我相信你看完了我的文章会有收获,或许对闭包有一个新的认识
在讲闭包之前,大家可以先看看我之前的文章,有关于调用栈的讲解,配合本篇文章食用效果更佳哦~
文章快捷入口:调用栈
首先我们先来看一道题
var arr = []
for (var i = 0; i < 10; i++){
arr[i] = function () {
console.log(i);
}
}
for (var j = 0; j < arr.length; j++){
arr[j]()
}
我们先来思考一下,最后会打印什么呢?让我们输出一下
可以看到,输出了10个10。这里为什么会输出10个10呢?当函数arr[j]调用时,要输出i的值,所以在arr[j]的执行上下文中寻找i,但是并没有找到i,接下来再它的外层作用域中寻找i,在这里也就是全局作用域,找到了i,在全局区中i的值通过for循环已经变成了10,所以当console.log(i)时,打印的是10个10。
那如果我们想要打印 0,1,2,3,4,5,6,7,8,9,我们将这段代码改一下,应该怎么改呢?这个问题我们先留在这,在我们学习了闭包之后,就可以轻松解决。
闭包
在JS中,根据词法作用域的规则,内部函数总是可以访问其外部函数中声明的变量的, 当内部函数被返回到外部函数之外时,即使外部函数执行结束了, 但是内部函数引用了外部函数的变量,那么这些变量依旧会被保存在内层中, 我们把这些变量的集合称为闭包
这个概念是不是有些难以理解呢?不用担心,我们接下来拿一道题来解释一下
function foo() {
var myName = '菌菌'
let test1 = 1
let test2 = 2
var innerBar = {
getName: function() {
console.log(test1);
return myName
},
setName: function (newName) {
myName = newName
}
}
return innerBar
}
var bar = foo()
bar.setName('来颗奇趣蛋')
console.log(bar.getName());
我们先来看一下输出结果
我们画调用栈的图来分析一下这道题
首先,全局执行上下文入栈
调用foo()函数,foo()执行上下文入栈
foo()函数执行完成,出栈
接下来,我们执行bar.setName('来颗奇趣蛋')
接下来我们发现,有一个赋值语句,myName = newName,可是,由于foo()已经出栈,bar.setName的执行上下文和全局区的执行上下文都没有myName这个变量,并且当bar.setName('来颗奇趣蛋') 执行完成出栈时,调用bar.getName()函数,它的执行上下文入栈,打印输出console.log(test1) 时,我们发现也找不到test01这个变量,那么最后为什么会输出1 和 '来颗奇趣蛋'呢?
其实,虽然foo()的执行上下文出栈了,但还是留下了一个'小书包',这里面存放了myName和test01,,如图
这里小伙伴们可能就要问了,那么多变量都不留,为什么就留这两个呢?这里我们来解释一下,因为setName()和getName()定义在了foo()里面,setName()和getName()是可以访问foo()里面的变量的,但可是setName()和getName()并不是在foo()里面调用的,而是在全局区调用的。
foo()创建了这两个函数,但并没有调用这两个函数,而是通过return返回一个对象。通俗一点来讲,当这两个函数在外部调用时,foo()并不能控制这两个函数,而这两个函数调用时,使用了foo()函数里的test1变量和myName变量,foo()函数不知道这两个函数什么时候会被调用,而当这两个函数调用时,它们会向foo()函数讨要test1和myName,所以foo()虽然出栈了,但是会把两个函数调用时需要使用的变量留下,当两个函数调用时,就可以直接来取。而图中黄色的框所包裹的变量,我们就称之为闭包。
看完了这个例子,我们是不是可以理解了开头那段概念。当内部函数(setName和getName被返回到外部函数(foo)之外时,这里也就是全局区,虽然foo()执行完毕了,但是之后两个函数执行时,引用了foo()内的变量,这些变量依旧会保存在内层之中,而这些被保存的变量的集合,我们就称之为闭包(closure)。
接下来我们再看一个例子来掌握一下:
function a() {
function b() {
var bbb = 234
console.log(aaa); //输出123
}
var aaa = 123
return b
}
var c = a()
c()
直接上图理解:
内部函数b()被返回到了外部函数a()之外,这里也是全局区,内部函数调用时,引用了a()中变量aaa,尽管a()已经出栈,但这些变量依旧会被保存下来,所以输出123.
我们也可以使用闭包的方法,让开头那个例子打印出0,1,2,3,4,5,6,7,8,9
var arr = []
for (var i = 0; i < 10; i++){
(function a() {
var j = i
arr[i] = function () {
console.log(j);
}
}) ()
}
for (var j = 0; j < arr.length; j++){
arr[j]()
}
总结
在JS中,根据词法作用域的规则,内部函数总是可以访问其外部函数中声明的变量的, 当内部函数被返回到外部函数之外时,即使外部函数执行结束了, 但是内部函数引用了外部函数的变量,那么这些变量依旧会被保存在内层中, 我们把这些变量的集合称为闭包
今天的内容就到这啦,如果你觉得小编写的还不错的话,或者对你有所启发,请给小编一个辛苦的赞吧
作者:来颗奇趣蛋
链接:https://juejin.cn/post/7297101248062144550
猜你喜欢
- 2025-06-23 JavaScript 开发者常犯的 10 个典型错误
- 2025-06-23 这7道问题可以检验你掌握Javascript的真正实力
- 2025-06-23 HarmonyOS NEXT 如何有效地进行内存管理和避免内存泄露?
- 2025-06-23 JS 函数柯里化(js函数柯里化面试题)
- 2025-06-23 《深入理解javascript原型和闭包系列》 知识点整理
- 2025-06-23 JS面试题突破:什么是闭包?(闭包 js)
- 2025-06-23 JavaScript闭包(javascript 闭包)
- 2025-06-23 “闭包到底是什么?90% 的前端开发者都没真正搞懂!”
- 2024-10-04 两个小案例让你深入理解 Javascript 闭包和作用域
- 2024-10-04 js中的闭包 js闭包的使用
你 发表评论:
欢迎- 06-24发现一款开源宝藏级工作流低代码快速开发平台
- 06-24程序员危险了,这是一个 无代码平台+AI+code做项目的案例
- 06-24一款全新的工作流,低代码快速开发平台
- 06-24如何用好AI,改造自己的设计工作流?
- 06-24濮阳网站开发(濮阳网站建设)
- 06-24AI 如何重塑前端开发,我们该如何适应
- 06-24应届生靠这个Java简历模板拿下了5个offer
- 06-24服务端性能测试实战3-性能测试脚本开发
- 561℃Oracle分析函数之Lag和Lead()使用
- 557℃几个Oracle空值处理函数 oracle处理null值的函数
- 546℃Oracle数据库的单、多行函数 oracle执行多个sql语句
- 543℃0497-如何将Kerberos的CDH6.1从Oracle JDK 1.8迁移至OpenJDK 1.8
- 539℃Oracle 12c PDB迁移(一) oracle迁移到oceanbase
- 531℃【数据统计分析】详解Oracle分组函数之CUBE
- 519℃最佳实践 | 提效 47 倍,制造业生产 Oracle 迁移替换
- 510℃Oracle有哪些常见的函数? oracle中常用的函数
- 最近发表
- 标签列表
-
- 前端设计模式 (75)
- 前端性能优化 (51)
- 前端模板 (66)
- 前端跨域 (52)
- 前端缓存 (63)
- 前端react (48)
- 前端aes加密 (58)
- 前端脚手架 (56)
- 前端md5加密 (54)
- 前端富文本编辑器 (47)
- 前端路由 (61)
- 前端数组 (73)
- 前端js面试题 (50)
- 前端定时器 (59)
- Oracle RAC (73)
- oracle恢复 (76)
- oracle 删除表 (48)
- oracle 用户名 (74)
- oracle 工具 (55)
- oracle 内存 (50)
- oracle 导出表 (57)
- oracle 中文 (51)
- oracle的函数 (57)
- 前端调试 (52)
- 前端登录页面 (48)
本文暂时没有评论,来添加一个吧(●'◡'●)