闭包并不是 JavaScript 特有的,大部分高级语言都具有这一能力。
什么是闭包?
A closure is the combination of a function bundled together (enclosed) with references to its surrounding state (the lexical environment).
这段是 MDN 上对闭包的定义,理解为:一个函数及其周围封闭词法环境中的引用构成闭包。可能这句话还是不好理解,看看示例:
function createAction() { var message = "封闭环境内的变量"; return function() { console.log(message); } } const showMessage = createAction(); showMessage(); // output: 封闭环境内的变量
这个示例是一个典型的闭包,有这么几点需要注意:
-
showMessage
是createAction
执行后从中返回出来的一个函数。 -
createAction
内部是一个封闭的词法环境,message
作为该封装环境内的变量,在外面是绝不可能直接访问。 -
showMessage
在createAction
外部执行,但执行时却访问到其内部定义的局部变量message
(成功输出)。这是因为showMessage
引用的函数(createAction
内部的匿名函数),在定义时,绑定了其所处词法环境(createAction
内部)中的引用(message
等)。 - 绑定了内部语法环境的匿名函数被
return
带到了createAction
封闭环境之外使用,这才能形成闭包。如果是在createAction
内部调用,不算是闭包。
好了,我相信 1, 2, 4 都好理解,但是要理解最重要的第 3 点可能有点困难 —— 困难之处在于,这不是程序员能决定的,而是由语言特性决定的。所以不要认为是“你”创建了闭包,因为闭包是语言特性,你只是利用了这一特性。
如果语言不支持闭包,类似上面的代码,在执行 showMessage
时,就会找不到 message
变量。我特别想去找一个例子,但是很不幸,我所知道的高级语言,只要能在函数/方法内定义函数的,似乎都支持闭包。
把局部定义的函数“带”出去
前面我们提到了可以通过 return
把局部定义的函数带出去,除此之外有没有别的办法?
函数在这里已经成为“货”,和其他货(变量)没有区别。只要有办法把变量带出去,那就有办法把函数带出去。比如,使用一个“容器”对象:
function encase(aCase) { const dog = "狗狗"; const cat = "猫猫"; aCase.show = function () { console.log(dog, cat); }; } const myCase = {}; encase(myCase); myCase.show(); // output: 猫猫 狗狗
是不是受到了启发,有没有联想到什么?
模块和闭包
对了,就是 exports 和 module.exports。在 CJS (CommonJS) 定义的模块中,就可以通过 exports.something
逐一带货,也可以通过 module.exports = ...
打包带货,但不管怎么样,exports
就是带货的那一个,只是它有可能是原来安排的 exports
也可能是被换成了自己人的 exports
。
ESM (ECMAScript Module) 中使用了 import
和 export
语法,也只不过是换种方法带货出去而已,和 return
带货差不多,区别只在于 return
只能带一个(除非打包),export
可以带一堆。
还要补充的是,不管是 CJS 还是 ESM,模块都是一个封装环境,其中定义的东西只要不带出去,外面是访问不到的。这和网页脚本默认的全局环境不同,要注意区别。
如果用代码来表示,大概是定义模块的时候以为是这样:
const var1 = "我是一个顶层变量吧"; function maybeATopFunction() { }
结果在运行环境中,它其实是这样的(注意:仅示意):
// module factory function createModule_18abk2(exports, module) { const var1 = "我是一个顶层变量吧"; function maybeATopFunction() { } } // ... 遥远的生产线上,有这样的示意代码 const module = { exports: {} }; const m18abk2 = createModule_18abk2(module) ?? module; // 想明白 createModule_18abk2 为什么会有一个随机后缀没?
还是那个函数吗?
扯远了,拉回来。思考一个问题:理论上来说,函数是一个静态代码块,那么多次调用外层函数返回出来的闭包函数,是同一个吗?
试试:
function create() { function closure() { } return closure; } const a = create(); const b = create(); console.log(a === b); // false
如果觉得意外,那把 closure()
换种方式定义看会不会好理解一点:
function create() { closure = function() { } return closure; }
如果还不能理解,再看这个:
function create() { const a = function () { }; const b = function () { }; console.log(a === b); // false }
能理解了不:每一次 function
都定义了一个新的函数。函数是新的,名字不重要 —— 你能叫小明,别人也能叫小明不是。
所以,总结一下:
闭包是由一个函数以及其定义时所在封闭环境内的各种资源(引用)构成,拿到的每一个闭包都是独一无二的,因为构成闭包的环境资源不同(不同的局部环境,定义了不同的局部变量,传入了不同的参数等)。
闭包,这回搞明白了!
推荐教程:《JS教程》
以上是js中闭包的概念的详细内容。更多信息请关注PHP中文网其他相关文章!

JavaScript是现代Web开发的核心语言,因其多样性和灵活性而广泛应用。1)前端开发:通过DOM操作和现代框架(如React、Vue.js、Angular)构建动态网页和单页面应用。2)服务器端开发:Node.js利用非阻塞I/O模型处理高并发和实时应用。3)移动和桌面应用开发:通过ReactNative和Electron实现跨平台开发,提高开发效率。

JavaScript的最新趋势包括TypeScript的崛起、现代框架和库的流行以及WebAssembly的应用。未来前景涵盖更强大的类型系统、服务器端JavaScript的发展、人工智能和机器学习的扩展以及物联网和边缘计算的潜力。

JavaScript是现代Web开发的基石,它的主要功能包括事件驱动编程、动态内容生成和异步编程。1)事件驱动编程允许网页根据用户操作动态变化。2)动态内容生成使得页面内容可以根据条件调整。3)异步编程确保用户界面不被阻塞。JavaScript广泛应用于网页交互、单页面应用和服务器端开发,极大地提升了用户体验和跨平台开发的灵活性。

Python更适合数据科学和机器学习,JavaScript更适合前端和全栈开发。 1.Python以简洁语法和丰富库生态着称,适用于数据分析和Web开发。 2.JavaScript是前端开发核心,Node.js支持服务器端编程,适用于全栈开发。

JavaScript不需要安装,因为它已内置于现代浏览器中。你只需文本编辑器和浏览器即可开始使用。1)在浏览器环境中,通过标签嵌入HTML文件中运行。2)在Node.js环境中,下载并安装Node.js后,通过命令行运行JavaScript文件。

如何在Quartz中提前发送任务通知在使用Quartz定时器进行任务调度时,任务的执行时间是由cron表达式设定的。现�...

在JavaScript中如何获取原型链上函数的参数在JavaScript编程中,理解和操作原型链上的函数参数是常见且重要的任�...

在微信小程序web-view中使用Vue.js动态style位移失效的原因分析在使用Vue.js...


热AI工具

Undresser.AI Undress
人工智能驱动的应用程序,用于创建逼真的裸体照片

AI Clothes Remover
用于从照片中去除衣服的在线人工智能工具。

Undress AI Tool
免费脱衣服图片

Clothoff.io
AI脱衣机

AI Hentai Generator
免费生成ai无尽的。

热门文章

热工具

ZendStudio 13.5.1 Mac
功能强大的PHP集成开发环境

Atom编辑器mac版下载
最流行的的开源编辑器

安全考试浏览器
Safe Exam Browser是一个安全的浏览器环境,用于安全地进行在线考试。该软件将任何计算机变成一个安全的工作站。它控制对任何实用工具的访问,并防止学生使用未经授权的资源。

SublimeText3 Linux新版
SublimeText3 Linux最新版

SublimeText3汉化版
中文版,非常好用