闭包是JavaScript中常见的概念,但其他各种语言也模拟了闭包的行为,包括Java、C++、Objective-C、C#、Golang等(但还是有区别的)来自传统的闭包)。之前对这个概念不是很清楚。希望通过看网上的资料,通过学习,掌握闭包的原理和应用场景。定义及初衷根据维基百科的溯源,闭包(closure)的概念最早由PeterLandin于1964年定义,用来描述在他的SECD机上求解表达式时的“环境部分”+“控制部分”。该术语用于指代一些开放绑定(自由变量)已被其周围的词法环境关闭(关闭或绑定)的Lambda表达式[1]。这个概念还是有些抽象,更明确的表述是MDNWeb社区对闭包的描述:一个函数与其周围状态(词法环境)的引用捆绑在一起,这样的组合称为闭包[2]。进一步的认识每个人都有不同的看法[3]。从定义的角度来看,闭包最大的用途实际上是将一个函数和它的一组“私有”变量捆绑在一起。在这个函数被多次调用的过程中,这些变量可以保持变化,不会被其他函数改变。保持一个变量的值多次使用并不难。核心价值是在保证这个变量的隐私的同时,对外界隐藏相关信息。这就涉及到变量的作用域,这也是JavaScript中的一大知识点。大多数材料也适用于并解释了JS中的闭包。ClosureInstanceandApplication官网教程中给出了一个清晰的例子来说明闭包的定义:functionmakeFunc(){varname="Mozilla";功能显示名称(){警报(名称);}returndisplayName;}varmyFunc=makeFunc();myFunc();首先,这段代码有效。直观上,varmyFunc=makeFunc()已经运行完了makeFunc()函数,但是后面的myFunc()调用之所以能正常处理,是因为形成了一个闭包。这里构成闭包的函数是displayName实例,绑定的变量是name。准确的说,变量名存放在displayName函数实例的词法环境中,一起形成一个闭包。因此,在调用myFunc时,变量名仍然可以被提醒。这是一个例子来说明这个概念。在实际应用场景中,闭包的使用方式有很多种。这里收集一些案例:JavaScript使用闭包来模拟私有方法https://developer.mozilla.org...varCounter=(function(){varprivateCounter=0;functionchangeBy(val){privateCounter+=val;}return{increment:function(){changeBy(1);},decrement:function(){changeBy(-1);},value:function(){returnprivateCounter;}}})();console.log(Counter.value());/*记录0*/Counter.increment();Counter.increment();console.日志(计数器值());/*记录2*/Counter.decrement();console.log(Counter.value());/*logs1*/独特之处在于可以使用该方法实现类似Java封装私有函数的场景,多个函数可以共享同一个词法环境(操作同一个私有变量)。Golang闭包和协程的使用让我又回到了闭包的例子,这是学习golang时在segmentfault中回答的一个问题(https://segmentfault.com/q/10...),协程的使用在如下代码中:因为我:=0;我<100;i++{gofunc(iint){fmt.Println(i)}(i)}和Golang学习网站的一个类似例子:https://books.studygolang.com...在Golang中,闭包体现为一个嵌套的匿名函数,其主要用途包括[8]:与JS类似,函数私有变量延长了变量的生命周期,隔离了变量与外界的访问。回调,就像任何其他语言一样。Wrapfunctions,制作“中间件”(middleware,Golang中的概念是可重用的函数)。在Golang中,函数是一等公民,函数可以作为其他函数中的参数,所以一些通用的处理逻辑,比如打印日志,定时器等,可以用闭包来实现。在很多库函数中,可以使用闭包来传入函数,以充分利用库函数带来的便利。例如,在sort包中,可以使用闭包中的函数来确定搜索对象的过滤条件。Java中的闭包和Lambda函数在Java8之后,语言生态不再只关注对象。很多时候,函数式编程的方法更加简洁轻量,所以引入了Lambda表达式。并且经常和“匿名函数”、“闭包”一起被提及。首先需要明确的是,Lambda函数基本上等同于匿名函数,但不等同于闭包。从源头也可以看出,闭包是计算Lambda表达式时引入的概念,不等于Lambda表达式本身。关于Lambda表达式和闭包本身区别的解释,请参考[10],里面有非常详细的描述。简单来说,Lambda表达式是一种简洁的程序编写方式,它涉及到开放式表达式——当表达式中的变量在函数外部时,需要使用闭包来分隔函数和外部参数在词法环境中的绑定一起计算。对于任何语言也是如此。intn=0;最终intk=n;//使用Java8不需要显式的finalRunnabler=()->{//使用lambdainti=k;//做点什么};n++;//现在不会产生errorr.run();//将以i=0运行,因为在创建lambda时k为0,其中变量k超出了Lambda表达式,需要使用闭包引入。值得注意的是,Java本身最好的写法是封装一个包含私有变量和私有函数的对象,不需要像JavaScript那样进行模拟。闭包的实现根据闭包的定义和期望的功能表现,我们可以看出,闭包背后通常的做法是使用一个数据结构,首先需要保存一个指向函数代码的指针,然后需要保存创建闭包时的词法环境,最典型的是保存创建时可用的所有变量。一个理想的可以实现闭包的语言,在它的运行时内存模型中,所有的原子变量都应该放在一个线性栈中。在这种语言场景下,如果你创建了一个闭包,那么在函数执行完之后,相应词法环境中的变量就不能被回收了。这时,典型的做法是将变量放入堆中(Java例子中可以和闭包一起使用的变量需要结合final修饰),直到所有闭包引用用完再回收。我想这也间接解释了网上广为流传的“IE中使用闭包会导致内存泄漏”问题的由来。闭包也更适合执行“垃圾收集”的语言。对于只对栈进行操作的语言,要实现闭包就没那么容易了,而且这些原子开发变量会出现野指针等问题。典型的基于栈的编程语言包括C和C++。具体到JavaScript,闭包的实现依赖于其变量作用域的相关机制,我还需要进一步研究JavaScript语言背后的内存模型。优点和缺点面对闭包,我们需要考虑的是是否使用它,以及是否有其他不使用闭包的替代方案。首先,闭包最大的优势已经体现在它的应用场景上,避免了全局变量的使用,通过函数将变量“私有化”,长期存储以供多次使用。然而,闭包也有其缺点。从原理和实现上看:闭包在其绑定的词法空间中主动延长相关变量的生命周期,这些变量会一直保存在内存中,直到用完,占用资源,一旦不处理好吧(比如IE),可能存在内存泄漏。其次,闭包的性能不好,一些特殊场景需要注意写法,比如[2]中的例子://badcase,themethodwillbeassignedpertimetheconstructoriscalledfunctionMyObject(name,message){this.name=name.toString();this.message=message.toString();this.getName=function(){返回this.name;};this.getMessage=function(){返回这个。信息;};}//好的情况,拆分出来functionMyObject(name,message){this.name=name.toString();this.message=message.toString();}MyObject.prototype.getName=function(){returnthis.name;};MyObject.prototype.getMessage=function(){returnthis.message;};参考https://en.wikipedia.org/wiki...(computer_programming)https://developer.mozilla.org...https://segmentfault.com/q/10...https://www.liaoxuefeng.com/w...https://zhuanlan.zhihu.com/p/...https://www.runoob.com/w3cnot...https://books.studygolang.com...https://www.calhoun.io/5-usef...https://riptutorial.com/java/...https://stackoverflow.com/que...
