JavaScript闭包到底有什么特别之处?
作者:赵刘伟
时间:2020-06-11
浏览量:
如果你是全栈,前端,后端或使用JavaScript进行任何操作,则可能听说过术语“闭包”。如果要面试软件工程师职位,则可能会遇到一个问题,要求你解释什么是闭包。上面提到的问题很容易回答,你仅记住闭包的定义就可以举几个例子。该术语的问题不在于理解闭包的定义,而是在于理解为什么要在项目中使用它。在深入探讨JavaScript闭包之前,让我们一起来了解一下什么是闭包。闭包是可以访问其定义的“外部”范围的函数。因此,即使闭包函数终止,它也可以访问外部作用域中的值。
function takeOne() { let i = 0; return function incrementFunction() { return i++; }}
上面的代码表示一个函数返回另一个函数。但是,调用takeOne并获取后incrementFunction,即使已经终止incrementFunction,takeOne也会记住的局部变量takeOne。
使用闭包的好处
闭包的第一个好处是将局部变量保留在范围内。由于JavaScript函数是一等公民,因此开发人员经常会遇到名称冲突,这将导致意外输出。使用闭包可以帮助将该范围内的名称空间保留为私有变量。你可以在过去的jQuery代码中看到很多东西,其中定义了click方法。
$(function(){ var selections = [] $(“。something”)。click(function(){ //此闭包可以访问外部变量selections selections.push(“ something”)// //获取外部函数选择 })})
尽管这确实是闭包的用例之一,但你可能会想:“这真的是闭包的目的吗?” 你可能仍然对有关闭包的一般用例是什么的说法提出疑问。第二个好处是更多在一般情况下使用,它在异步环境中很有用。
for(var i = 0; i <3; i ++){ setTimeout(()=> console.log(i),3000)}
它打印3三遍。由于setTimeout是异步的,所以在循环结束时,外部作用域i也已更改为3,并且setTimeout在循环期间对后续调用将触发回调并3次打印。有很多方法,包括使用ES6语法let而不是var在块级别定义其范围并解决问题。但是,如果他们希望你在不使用任何ES6功能的情况下解决此问题,则闭包是你的答案。
function printSomething(i) {
setTimeout(() => console.log(i), 3000)
}
for(var i = 0; i<3; i++) {
printSomething(i)
}
通过仅在外部创建另一个外部函数setTimeout,就可以定义一个闭包。该i值即使printSomething终止也将保留。然后,回调将打印0 、1、 2到控制台。这就是JavaScript闭包强大的功能所在。你可以使用闭包在异步环境中保留外部变量的范围。
再举另一个例子
让我们想象一下,另外一个例子,你需要创建一个函数,该函数需要调用第三方API并汇总结果并将其返回给调用方。
function getAPI(cb) {
setTimeout(() => cb("a"), 3000)
}
function getAPIB(cb) {
setTimeout(() => cb("b"), 2000)
}
function getAPIC(cb) {
setTimeout(() => cb("c"), 1000)
}
function aggregateValue() {
var aggregateData = []
// your implementation here
}
在继续阅读解决方案之前,请先暂停片刻,然后思考如何在没有承诺或异步/等待的情况下解决此问题。我们可以利用闭包的功能来保留函数的范围,并aggregateValue通过使用回调来停止返回。
function aggregateValue(cb) {
var aggregateData = []
var numberAPICalledSoFar = 0
function callback(value) {
aggregateData = [...aggregateData, value]
if(numberAPICalledSoFar < 2) {
numberAPICalledSoFar++;
}else {
cb(aggregateData)
}
}
getAPI(callback)
getAPIB(callback)
getAPIC(callback)
}
因为getAPI,getAPIB,getAPIC都使用一个回调函数,你可以创建一个回调函数,称为迄今为止的API数量的增加数量。一旦调用的API数量超过2,则调用返回回调值。上面的代码再次利用了闭包的功能来在触发闭包函数时保留其局部变量。作为getAPI完成其打电话,唤起了回调函数,回调函数访问外部范围aggregateValue递增计数的API完成执行的数量。在aggregateData随后与来自外部的回调返回aggregateValue,从所有的第三方API需要所有的汇集数据的功能。aggregateValue((ans) => ans.foreach(console.log))
总结
闭包是javaScript的“更高级”功能。闭包的一般用例在于在异步环境中求解计算。