闭包在JavaScript中是如何工作的,为什么它有用?

本文来自:IT宝库(https://www.itbaoku.cn)

推荐答案1

函数onclickhandler(){

让count = 0;

return function(){

count ++;

console.log(`按钮单击$ {count} times`);

};

}

let clickcounter = onclickhandler();

//稍后,响应按钮单击

clickcounter();//输出:按钮单击1次

JavaScript中的封闭提供了一种有力的机制,用于维护状态,创建私有变量和支持功能编程模式.它们增强了语言的灵活性和表现力.

推荐答案2

在深入JavaScript闭合之前,让我们讨论有关词汇范围,

javascript遵循词汇范围,这意味着 函数在定义时会创建其范围,而不是在执行它们时 .在这里,范围不过是可以访问变量的程度,如果可以访问变量的程度仅在函数中,则称为 local Scope ,以及如果可以在程序中的任何位置访问该变量,称为 全局范围. 词汇范围说, 函数不共享本地范围 即,在一个函数中定义的变量在另一个函数中无法访问.<<<<<<<<<<<<<<<<<<<<<

以下示例说明了JavaScript词汇范围,

在上述JavaScript代码中,我们正在尝试显示增加两个数字.在此代码中,add()函数在数字()函数中被调用,我们可能会认为,由于add()函数在numbers()中调用,因此add()也可以访问诸如A,FirstNum和secondNum之类的变量,但是这是错误的,因为javaScript遵循词汇范围,即范围创建取决于函数的定义位置而不是在何处执行/调用函数.这就是为什么add()在数字()()( 的变量)上没有访问的原因,即使add()被调用Inside() ),因为它是在数字之外定义的( ). 该程序的输出将未定义. 由于JavaScript按行翻译了行,因此在第7行中遇到了错误的错误,该错误未定义并显示.

克服了这一问题由于词汇范围, 闭合 出现 .闭合 不过是一个函数,除了其变量和全局变量外,还可以访问其他函数的变量.在JavaScript中,这种功能仅受私有/内部功能支持,因此,闭合不过是内部功能.一个人可以通过在另一个函数中添加功能来创建闭合.

上面的问题通过以下内容解决,

解决. 在这种情况下,add()是在数字()内定义的,并且可以访问所有变量.输出将为:结果为7.

闭合有4个点,

  • 闭合即使在外部函数返回之后也可以访问外部功能变量.
  • 闭合实际上存储了外部函数变量的参考,而不是实际值.
  • 闭合即在全局范围中无法调用内部函数,因为根据词汇范围,它只能在外部函数中访问.
  • 一个人可以将关闭封闭到全局通过返回功能定义闭合外部功能的范围.

推荐答案3

取决于实现.实现封闭的方法有几种不同的方式,它们与语言实现者的方式相互作用也可以选择在嵌套范围中实现可变变量.

V8 JavaScript引擎的方式但是,Node.js和Chrome)实现封闭是相当典型的,并且不难理解.

首先,让我们看一下一个天真且效率低下的,但功能性的,可以完成的方式:

每当调用函数时,都会分配一个对象以保存所有函数的局部变量.通常,这是在堆栈上完成的,我们将该对象称为"堆栈框架"(以及返回地址(如返回地址)),并且只要函数返回,它就会被覆盖,并在此之后调用新功能.但是,您没有 可以在堆栈上分配变量存储.与其将局部变量存储在堆栈框架中,而是可以为它们在堆上分配一个对象.这样,当创建它们的函数返回时,本地变量不必被删除或覆盖 - 它们可以在必要时坚持下去. ,每当创建封闭时,都会引用存储所有局部上下文变量的对象.当所有引用关闭都消失时,垃圾收集器可以清理该变量上下文对象.但是,只要闭合可以访问,它就可以从创建的封闭环境中访问变量.每个上下文对象还包含对下一个较高范围的上下文对象的引用,因此总有一个链条,只要可能需要,就可以从任何任意更高的范围中访问发动机访问变量.它只需要记住,对于引用变量的每个地方,必须回到多少链接才能找到保存该变量内存的正确上下文对象.

现在,这有效,但是正如我所说的那样,这不是很高效.每当创建封闭时,它都会为当时每个较高的词汇范围进行对每个上下文对象的引用,这可以防止大量内存无缘无故地收集垃圾.这也意味着所有变量访问都必须经过指针间接范围,从而使事情变慢.有很多改进的方法,但是V8实施的一种相当简单的方法如下:

解析代码时,V8跟踪每个变量的位置引用,并使用该信息将每个函数的本地变量分为两个类:嵌套函数中引用的函数(即,可以从封闭式访问的功能)和那些不访问的函数(即那些可以访问的函数)切勿从封闭中访问).这使其可以在常规堆栈框架中分配非关闭变量,该变量会自动清理并在每个新功能调用上重复使用.但是,如果有任何封闭变量,它将在堆上的上下文对象中分配 这些 ,这可能从较高的范围中指向另一个上下文对象(就像以前一样)如果来自较高范围的变量在当前范围中也关闭.

,这意味着发动机只需要在将来实际上可以在将来实际上使用的变量堆,可以节省一堆内存.此外,它只需要使用指针间接方向来用于封闭中实际使用的变量 - 堆栈中可以访问其他所有内容,这可以使其快速.

在很短的情况下:v8算出哪些变量可以在关闭中访问哪些变量并将它们存储在堆上而不是堆栈中,这意味着所有对这些变量的访问都放慢了(这在紧密循环和其他热门代码中很重要),但是它们可以生存下来无限期,只要任何封闭都可能需要它们.

推荐答案4

参考.

推荐答案5

闭合是JavaScript中强大的工具.它通常用于功能编程语言中,但经常被误解.像其他JavaScript基本原理一样,理解封闭是写出表达,简洁和可维护的脚本所必需的.

什么是关闭?

乍一看,闭合只是在另一个函数中定义的函数.但是,封闭的力量来自以下事实:内部函数记住创建的环境.换句话说,内部函数可以访问外部函数的变量和参数.

它是什么样的?下面是闭合的示例( courtesy of Mozilla ):

  1. 12345678 
  2. function pam() { var name = "Pam Beesly"; function displayName() { alert (name); } displayName();}pam(); 

我们的外部功能 - pam - 做三件事:

  1. 定义本地变量, name
  2. 定义函数, displayName
  3. 呼叫 displayName displayName

displayName 没有定义任何局部变量,但它能够提醒 name ,因为 name 在创建关闭的范围(其外部功能的范围)中定义他们也是.在下面观察:

  1. 12345678910111213 
  2. function pam() { var name = "Pam Beesly"; function displayName() { alert (name); } function setName(newName) { name = newName; } displayName(); setName("Pam Halpert"); displayName();}pam(); 

正如我们所看到的,闭合不仅能够阅读,而且还可以操纵其外部功能的变量./span>

功能工厂

一种强大的闭合用途是将外部函数用作工厂来创建与某种相关的功能.

  1. 123456789101112 
  2. function dwightJob(title) { return function(prefix) { return prefix + ' ' + title; };}var sales = dwightJob('Salesman');var manager = dwightJob('Manager');alert(sales('Top')); // Top Salesmanalert(manager('Assistant to the Regional')); // Assistant to the Regional Manageralert(manager('Regional')); // Regional Manager 

使用闭合作为功能工厂是保持javaScript干燥的好方法.五行强大的代码行允许我们创建具有相似但唯一目的的任何数量的功能.

namespacing private functions

许多面向对象的语言提供了将方法声明为公共或私人的能力. JavaScript没有内置此功能,但是它确实允许通过使用闭合来模拟此功能,该功能称为模块模式.

  1. 1234567891011121314151617181920212223242526 
  2. var dwightSalary = (function() { var salary = 60000; function changeBy(amount) { salary += amount; } return { raise: function() { changeBy(5000); }, lower: function() { changeBy(-5000); }, currentAmount: function() { return salary; } };})();alert(dwightSalary.currentAmount()); // $60,000dwightSalary.raise();alert(dwightSalary.currentAmount()); // $65,000dwightSalary.lower();dwightSalary.lower();alert(dwightSalary.currentAmount()); // $55,000dwightSalary.changeBy(10000) // TypeError: undefined is not a function 
< i8>

使用闭合到名称空间私有功能可以使更多的通用名称空间保持清洁,从而防止命名碰撞. salary 变量或 changeBy 均无法在 dwightSalary 之外使用.但是, raise lower currentAmount 都可以访问它们,并且可以在 .

这些是关闭的一些流行用途.您肯定会遇到用于其他目的的封闭,但是这些是几种简单的方法,可以立即有用的方式将封闭式纳入代码.

您可以找到原始帖子态