86个答案
A函数和 对该函数外部范围(词汇环境)的引用
函数foo(){ const secret=数学.trunc(Math.random()*100) 返回函数inner(){ console.log(`机密编号为${secret}。`) } } const f=foo()//`secret`不能从外部直接访问`foo` f()//检索“secret”的唯一方法是调用“f”`
封口的使用
私有实例变量
功能车(制造商、型号、年份、颜色){ 返回{ toString(){ 返回`${manufacturer}${model}(${year},${color})` } } } const car=新车('Aston Martin'、'V8 Vantage'、'2012年'、'Quantum Silver') console.log(car.toString())
函数式编程
函数curry(fn){ 常量参数=[] 内部返回函数(arg){ 如果(args.length===fn.length)返回fn(…args) 参数推送(arg) 返回内部 } } 函数加法(a,b){ 返回a+b } const curriedAdd=咖喱(添加) console.log(curriedAdd(2)(3)())//5
面向事件的编程
const$=document.querySelector.bind(document) const BACKGROUND_COLOR=“rgba(200,200,242,1)” onClick()函数{ $('body').style.background=背景色 } $(“按钮”).addEventListener(“单击”,onClick)
<button>设置背景颜色</button>
模块化
让名称空间={}; (函数foo(n){ 让数字=[] 函数格式(n){ return数学trunc(n) } 函数tick(){ numbers.push(数学.random()*100) } 函数toString(){ return numbers.map(格式) } n.计数器={ 勾选, toString(字符串) } }(命名空间)) 常量计数器=命名空间计数器 计数器刻度() 计数器刻度() console.log(counter.toString())
示例
示例1
函数foo(){ 设x=42 let内部=()=>console.log(x) x=x+1 返回内部 } foo()()//日志43
示例2
函数createObject(){ 设x=42; 返回{ log(){console.log(x)}, increment(){x++}, 更新(值){x=value} } } const o=创建对象() o.增量() o.log()//43 o.update(更新)(5) o.log()//5 常量p=创建对象() p.log()//42页
示例3
函数foo(){ var结果=[] 对于(var i=0;i<3;i++){ push(函数inner(){console.log(i)}) } 返回结果 } 常量结果=foo() //以下内容将打印“3”三次。。。 对于(var i=0;i<3;i++){ 结果[i]() }
最后一点:
每当在JavaScript中声明函数时,都会创建闭包。 返回 功能 从内部来看,另一个函数是closure的经典示例,因为即使在外部函数完成执行之后,外部函数内部的状态也可以隐式地供返回的内部函数使用。 无论何时使用 评估() 在函数内部使用闭包。 你的文字 评估 可以引用函数的局部变量,在非限定模式下,甚至可以使用 eval('var foo=…') . 当您使用 新建函数(…) (该 函数构造函数 )在函数内部,它不会在其词汇环境中关闭:而是在全局上下文中关闭。 新函数不能引用外部函数的局部变量。 JavaScript中的闭包类似于保留引用( 不是 一个副本)到函数声明点的作用域,它反过来保持对其外部作用域的引用,以此类推,一直到作用域链顶部的全局对象。 声明函数时创建闭包; 此闭包用于在调用函数时配置执行上下文。 每次调用函数时都会创建一组新的局部变量。
链接
道格拉斯·克罗福德的模拟 私有属性和私有方法 对于对象,使用闭包。 对闭包如何能够 导致IE内存泄漏 如果你不小心的话。 MDN文档 JavaScript闭包 . 初学者指南 JavaScript闭包 .
-
11 我只做了六年的前端开发人员,所以我很好奇常见的例子 咖喱添加(2)(3)() 在解释闭包或编码访谈时,除了函数编程示例之外。 我做过很多代码审查,但从来没有遇到过,但我也从来没有像我想象的那样与计算机科学MVP合作过。 – 跨域访问 评论 2022年3月14日14:26 -
毫无疑问,你写的是真的,尽管我还没有亲自核实。 然而,我觉得这很令人不安。你能做一个编辑来演示如何打印0、1和2吗。 另外,在两个循环中,i都小于3,那么它怎么可能打印3呢? 它是否经过并在循环结束后执行。 注意:在我学习javascirpt之前,我先是从“C”开始,然后是C++,最后是C#背景。 – 约瑟夫多吉 评论 2023年4月20日14:53 -
函数foo(x){ var tmp=3; 功能栏(y){ 控制台.log(x+y+(++tmp));// 将记录16 } 巴(10); } foo(2);
函数foo(x){ var tmp=3; 返回函数(y){ 控制台.log(x+y+(++tmp));// 也将记录16 } } var bar=foo(2); 巴(10);// 16 巴(10);// 17
变量a=10; 功能测试(){ 控制台.log(a);// 将输出10 console.log(b);// 将输出6 } var b=6; 测试();
就像老阿尔伯特说的那样:“如果你不能向一个六岁的孩子解释,你就真的不懂。”我试着向一个27岁的朋友解释JS闭包,但完全失败了。
有人会认为我今年6岁,对这个话题很感兴趣吗?
函数princes(){
var冒险=[]; 函数princeCharming(){/*…*/} var unicorn={/*…*/}, 龙=[/*…*/], 松鼠=“你好!”; /* ... */
返回{
故事:函数(){ 返回冒险[冒险.长度-1]; } }; }
var littleGirl=公主();
littleGirl.story();
-
390 我真的很喜欢这个解释。 对于那些读过它而没有读过它的人来说,类比是这样的:princes()函数是一个包含私有数据的复杂作用域。 在函数外部,无法查看或访问私有数据。 公主将独角兽、龙、冒险等保存在她的想象中(私人数据),大人们自己看不到它们。 但是公主的想象力被捕捉到了 故事() 函数,这是 小女孩 实例暴露在魔法世界中。 – 帕特里克·M 评论 2013年2月28日7:49 -
4 -
三 白马王子可以增加她的冒险经历,可以杀死所有的龙,从而将她从如下危险中解救出来: function princeCharming{adventures.push(“蜜月之旅”、“跳伞”、“访问索马里”);const pickADragonToKill=dragons.pop();} – Shiv公司 评论 2021年1月13日5:15 -
1 我理解的一个关键点是添加 console.log(小女孩) @Hugolpz回答。 当我用devtools在littleGirl对象中钻孔时,我在任何地方都找不到princeCharming、独角兽、龙或松鼠。 – 布拉德利奥 评论 2022年12月19日9:42
您的孩子将能够遵循两步指导。 例如,如果你对你的孩子说,“去厨房给我拿个垃圾袋”,他们就会记住这个方向。
厨房是一个包含局部变量的闭包,称为
垃圾袋 。厨房里有一个功能叫做
获取垃圾袋 他拿了一个垃圾袋并把它还了。
函数makeKitchen(){ var垃圾袋=['A','B','C'];// 开始只有3个 返回{ 获取垃圾袋:函数(){ return trashBags.pop(); } }; } var厨房=makeKitchen(); console.log(kitchen.getTrashBag());// 返回垃圾袋C console.log(kitchen.getTrashBag());// 返回垃圾袋B console.log(kitchen.getTrashBag());// 返回垃圾袋A
每次 制作厨房() 则创建一个新的闭包,并使用它自己的 垃圾袋 . 这个 垃圾袋 变量是每个厨房内部的局部变量,在外部无法访问,但在 获取垃圾袋 属性确实可以访问它。 每个函数调用都会创建一个闭包,但没有必要保留闭包,除非可以从闭包外部调用可以访问闭包内部的内部函数。 使用返回对象 获取垃圾袋 函数在这里执行此操作。
稻草人
相当明显的解决方案
//声明事件处理程序范围之外的计数器 var计数器=0; var元素=document.getElementById('按钮'); element.addEventListener(“单击”,function(){ //递增外部计数器 计数器++; if(计数器===3){ //每隔三次做一件事 log(“第三次很有魅力!”); //重置计数器 计数器=0; } });
<button id=“button”>点击我</ 按钮>
考虑这个选项
var元素=document.getElementById('按钮'); element.addEventListener(“点击”,(function(){ //将计数初始化为0 var计数=0; 返回函数(e){//<-此函数成为click处理程序 count++;// 并将保留对上述“count”的访问权限` 如果(计数===3){ //每隔三次做一件事 log(“第三次很有魅力!”); //重置计数器 计数=0; } }; })());
<button id=“button”>点击我</ 按钮>
//_______________________立即调用______________________ // | | //|保留使用范围____作为____返回| //|仅通过返回的函数|函数的值|| // | | | | | | //vv v v v vv v var func=(function(){var a='val';返回函数(){alert(a);};}) ();
func();// 警报“val” 函数a;// 未定义
const makePlus=函数(x){ 返回函数(y){return x+y;}; } const plus5=makePlus(5); 控制台.log(加5(3));
控制台.log(x+3);
函数f(){}
函数g(){ 函数h(){} }
函数i(){ var x='mochacchino' 返回函数j(){ console.log('从函数j:中打印x的值',x) } } 常数k=i() setTimeout(k,500)//500毫秒后调用k(即j)
函数l(){ var y='香草'; 返回{ setY:函数(值){ y=值; }, logY:函数(值){ console.log('y的值是:',y); } } } 常数o=l() o.logY()//y的值是:香草 o.setY(巧克力) o.logY()//y的值是:巧克力
var平面=功能(默认机场){ var lastAirportLeft=默认机场; var汽车={ 驱动程序:{ startAccessPlaneInfo:function(){ setInterval(函数(){ console.log(“Last airport was”+lastAirportLeft); }, 2000); } } }; car.driver.startAccessPlaneInfo(); 返回{ 离开机场:功能(airPortName){ lastAirportLeft=机场名称; } } }(“Boryspil国际机场”); 飞机。离开机场(“约翰·肯尼迪”);
闭包不仅是在返回内部函数时创建的。 实际上,封闭函数 根本不需要返回 以便创建其闭包。 相反,您可以将内部函数赋给外部范围中的变量,或者将其作为参数传递给另一个函数,在该函数中可以立即调用或稍后随时调用。 因此,可能会创建封闭函数的闭包 只要调用了封闭函数 因为无论何时调用内部函数,在封闭函数返回之前或之后,任何内部函数都可以访问该闭包。 闭包不引用 旧值 其范围内的变量。 变量本身是闭包的一部分,因此访问其中一个变量时看到的值是访问时的最新值。 这就是为什么在循环内部创建的内部函数可能很棘手,因为每个函数都可以访问相同的外部变量,而不是在创建或调用函数时获取变量的副本。 闭包中的“变量”包括任何命名函数 在函数中声明。 它们还包括函数的参数。 闭包还可以访问其包含闭包的变量,一直到全局范围。 闭包使用内存,但不会导致内存泄漏 因为JavaScript本身会清理自己未被引用的循环结构。 当Internet Explorer无法断开引用闭包的DOM属性值时,就会创建涉及闭包的内存泄漏,从而维护对可能循环结构的引用。
闭包是让函数具有 持久的私有变量 -也就是说,只有一个函数知道的变量,它可以在哪里跟踪以前运行时的信息。
例子
var iplus1=(函数() { var plusCount=0; 返回函数() { return++plusCount; } })();
如果你不能向一个六岁的孩子解释,那你自己也真的不明白。
关闭很简单:
函数make_calculator(){ 变量n=0;// 这个计算器只存储一个数字n 返回{ 添加:函数(a){ n+=a; 返回n; }, 乘法:函数(a){ n*=a; 返回n; } }; } first_calculator=生成计算器(); second_calculator=生成计算器(); first_calculator.add(3);// 返回3 second_calculator.add(400);// 返回400 first_calculator.乘法(11);// 返回33 second_calculator.乘法(10);// 返回4000
你能给一个5岁的孩子解释一下关门的原因吗*
/* *当一个函数在另一个函数中定义时 *即使在之后也可以访问外部函数的上下文 *外部函数返回。 * *JavaScript中需要学习的重要概念。 */ 函数outerFunction(someNum){ var someString=“嘿!”; var content=文档.getElementById('content'); 函数innerFunction(){ content.innerHTML=someNum+':'+someString; content=null;// DOM引用的Internet Explorer内存泄漏 } innerFunction(); } 外部函数(1);
正确关闭:
console.log('CLOSURES DONE RIGHT'); var arr=[]; 函数createClosure(n){ 返回函数(){ 返回'n='+n; } } for(var索引=0;索引<10;索引++){ arr[index]=创建闭合(索引); } for(arr的var指数){ 控制台.log(arr[索引]()); }
在上述代码中 创建关闭(n) 在循环的每次迭代中调用。 注意,我将变量命名为 n个 强调它是一个 新的 在新函数范围中创建的变量,与 指数 它被绑定到外部范围。 这将创建一个新范围并 n个 受该范围约束; 这意味着我们有10个单独的作用域,每个迭代一个作用域。 创建关闭(n) 返回返回该范围内n的函数。 在每个范围内 n个 绑定到它在何时具有的任何值 创建关闭(n) 被调用,因此返回的嵌套函数将始终返回 n个 那是什么时候 创建关闭(n) 已调用。
关闭错误:
console.log('CLOSURES DONE WRONG'); 函数createClosureArray(){ var badArr=[]; for(var索引=0;索引<10;索引++){ badArr[index]=函数(){ 返回'n='+索引; }; } 返回badArr; } var badArr=创建闭包数组(); for(badArr的var索引){ console.log(badArr[索引]()); }
在上述代码中,循环在 创建ClosureArray() 函数和函数现在只返回完成的数组,乍一看似乎更直观。 不太明显的是 创建ClosureArray() 仅在为该函数创建一个作用域时调用,而不是为循环的每个迭代创建一个。 在此函数中,一个名为 指数 定义。 循环运行并将函数添加到返回 指数 。请注意 指数 定义在 创建ClosureArray 只被调用一次的函数。 因为在 创建ClosureArray() 功能, 指数 仅绑定到该范围内的值。 换句话说,每次循环更改 指数 ,它会更改该范围内引用它的所有内容。 添加到数组中的所有函数都返回SAME 指数 变量来自定义它的父范围,而不是像第一个示例那样来自10个不同范围的10个不同的变量。 最终结果是,所有10个函数都从同一范围返回相同的变量。 循环完成后 指数 结束值为10,因此添加到数组中的每个函数都返回单个函数的值 指数 变量,现在设置为10。
结果
正确完成结算 n=0 n=1 n=2 n=3 n=4 n=5 n=6 n=7 n=8 n=9
关闭操作错误 n=10 n=10 n=10 n=10 n=10 n=10 n=10 n=10 n=10 n=10
在计算机科学中,闭包是一个函数以及该函数的非局部名称(自由变量)的引用环境。
var db=(函数(){ //创建一个隐藏对象,它将保存数据 //从外面是无法接近的。 var数据={}; //制作一个函数,它将提供对数据的一些访问。 返回函数(key,val){ if(val===未定义){return data[key]}//获取 else{return data[key]=val}//设置 } //我们正在调用匿名环绕函数, //返回上面的内部函数,它是一个闭包。 })(); db('x')//->未定义 db('x',1)//将x设置为1 db('x')//->1 //无法访问数据对象本身。 //我们能够获得或设置个人信息。
孩子们永远不会忘记他们与父母分享的秘密,即使他们的父母 跑了。 这就是函数的闭包。
var父级=函数(){ var name=“玛丽”;// 秘密 }
var父级=函数(){ var name=“玛丽”; var child=函数(childName){ //我还可以看到“名字”是“玛丽” } }
var父级=函数(){ var name=“玛丽”; var child=函数(childName){ return“我的名字是”+childName+“,是”+name的孩子; } 返回子项;// 孩子离开父母-> } var child=parent();//<- 它在外面
child(“Alice”)=>“我叫Alice,玛丽的孩子”
闭包不仅是在返回内部函数时创建的。 事实上,封闭函数根本不需要返回。 相反,您可以将内部函数赋给外部范围中的变量,或将其作为参数传递给另一个可以立即使用它的函数。 因此,封闭函数的闭包可能在调用封闭函数时已经存在,因为任何内部函数在调用时都可以访问它。
变量i; 函数foo(x){ var tmp=3; i=函数(y){ 控制台.log(x+y+(++tmp)); } } foo(2); i(3);
//makeSequencer将返回一个“sequencer”函数 var makeSequencer=函数(){ var计数=0;// 在此功能外无法访问 var序列器=函数(){ return _ count++; } 返回序列器; } var fnext=makeSequencer(); var v0=fnext();// v0=0; var v1=fnext();// v1=1; var vz=fnext_ count//vz=未定义
首先,创建函数f时,它不是在空的 空间。 存在当前的LexicalEnvironment对象。 在这种情况下 上面,它是窗口(a在运行时未定义 创建)。
内部函数保持对外部函数的引用 词汇环境。 内部函数可以从中访问变量 任何时候,即使外部功能完成。 浏览器将LexicalEnvironment及其所有属性(变量)保存在内存中,直到有一个内部函数引用它。
功能过夜(HowManyControllers ToBring){ var numberOfDansController=howManyControllersToBring; 返回函数danInvitedPaul(PaulsControllers的数量){ var totalControllers=数字OfDansController+数字OfPaulsController; 返回totalControllers; } } var howManyControllersToBring=1; var inviteDan=sleepOver(howManyControllersToBring); //保罗被邀请的唯一原因是丹被邀请了。 //所以我们把保罗的邀请设置为丹的邀请。 var danInvitedPaul=邀请Dan(howManyControllersToBring); 警报(“有”+danInvitedPaul+“控制员被带到派对”);
论据 局部变量(即它们的局部变量和局部函数) 环境,包括: 全局变量,包括DOM 外部函数中的任何内容
var isVotedUp=false; var isVotedDown=false; 函数voteUp_click(){ if(isVotedUp) 回报; else if(isVotedDown) SetDownVote(false); 其他的 SetUpVote(true); } 函数voteDown_click(){ 如果(isVotedDown) 回报; else if(isVotedUp) SetUpVote(false); 其他的 SetDownVote(true); } 功能SetUpVote(状态){ isVotedUp=状态; //对“向上投票”按钮执行一些CSS操作 } 功能SetDownVote(状态){ isVotedDown=状态; //对“投票”按钮执行一些CSS操作 }
功能演唱(人){ var firstPart=“有”+个人+“吞咽者”; var fly=函数(){ var生物=“一只苍蝇”; var result=“也许她会死”; 警报(第一部分+生物+“\n”+结果); }; var spider=函数(){ var生物=“蜘蛛”; var result=“她体内的那个东西扭动着,抖动着,痒痒着”; 警报(第一部分+生物+“\n”+结果); }; var bird=函数(){ var生物=“一只鸟”; var result=“多么荒谬!”; 警报(第一部分+生物+“\n”+结果); }; var cat=函数(){ var生物=“一只猫”; var result=“想象一下!”; 警报(第一部分+生物+“\n”+结果); }; fly(); 蜘蛛(); 鸟(); cat(); } var person=“一位老太太”; 唱歌(人);
函数cookMeal(){/*函数内部填充*/}
函数sing(person){/*函数内部的填充*/}
var person=“一位老太太”;
唱歌(人);
“来吧 唱 ,快来拿 人 ! "
fly(); 蜘蛛(); 鸟(); cat();
想象一下,你在整个房子里和你的小兄弟姐妹们玩耍,你拿着玩具四处走动,把其中一些玩具带进了你哥哥的房间。 过了一会儿,你哥哥从学校回来,回到他的房间,他锁在里面,所以现在你再也不能直接拿到那里剩下的玩具了。 但你可以敲门向你哥哥要玩具。 这叫玩具 关闭 ; 你哥哥为你编造的,现在他已经到了外面 范围 .
兄弟会室内功能游戏(带玩具){ //我们把在哥哥房间里玩的玩具收起来。 当他回来锁上门时 //你哥哥现在应该进入外部[[scope]]对象了。 感谢上帝,你可以和他交流。 var closureToys=带玩具||[], returnToy,countIt,玩具;// 只是另一个闭合助手,供兄弟内部使用。 var brotherGivesToyBack=函数(玩具){ //新请求。 兄弟手里还没有closureToys。 给他点时间。 returnToy=空; 如果(toy&&closureToys.length>0){//如果我们要一个特定的玩具,兄弟会去搜索它。 for(countIt=closureToys.length;countIt;countIt--){ if(closureToys[countIt-1]==玩具){ returnToy='带上你的'+closureToys。splice(countIt-1,1)+',小男孩!'; 断裂; } } returnToy=returnToy ||“嘿,我在这里找不到任何“+玩具+”。 在另一个房间里找; } 否则,如果(closureToys.length>0){//否则,只需归还他在房间里的所有东西。 returnToy=“看!”+ closureToys.join(',')+'.'; closureToys=[]; } 其他{ returnToy=“嘿,小虾,我给了你一切!”; } console.log(returnToy); } return兄弟GivesToyBack; } //你正在房子里玩,包括哥哥的房间。 var toys=['tedybear','car','jumpingrope'], askBrotherForClosuredToy=在BrothersRoom中玩(玩具); //门被锁上了,哥哥从学校回来了。 你不能作弊,直接拿出来。 console.log(askBrotherForClosuredToy.closureToys);// 未定义 //但你可以礼貌地要求你哥哥把它还给我。 askBrothersForClosureToy(已关闭);// 万岁,在这里,泰迪熊 askBrotherForClosuledToy(“球”);// 哥哥找不到它。 askBrotherForClosuledToy();// 剩下的都是哥哥给你的 askBrotherForClosuledToy();// 里面什么都没有
函数foo(initValue){ //当foo函数退出时,此变量不会被销毁。 //它由下面返回的两个嵌套函数“捕获”。 var值=initValue; //请注意,两个返回的函数是立即创建的。 //如果再次调用foo函数,它将返回 //引用不同“值”变量的新函数。 返回{ getValue:function(){return value;}, setValue:函数(newValue){value=newValue;} } } 功能栏(){ //foo将其局部变量“value”设置为5,并返回一个带有 //两个函数仍引用该局部变量 var obj=foo(5); //提取函数只是为了表明这里不涉及“this” var getValue=obj.getValue; var setValue=对象设置值; 警报(getValue())// 显示5 设置值(10); 警报(getValue())// 显示10 //此时getValue和setValue函数被销毁 //(实际上,它们在垃圾收集器的下一次迭代中被销毁)。 //foo中的局部变量“value”不再由引用 //任何东西都会被摧毁。 } bar();
-
没有人这么说,但你可能是对的。 此外,必须理解,JavaScript中的局部变量不是在堆栈框架上创建的,而是在堆上创建的并且只有在没有人引用它们时才被销毁 所以我只是告诉大家,我听说原始数据类型存储在堆栈上,而非原始数据类型则存储在堆上,您的意思是说所有数据类型都存储在堆中,而堆栈从未用于任何变量吗? 只是问问 – 优素福 评论 2023年11月8日18:01 -
1 变量的实际存储方式是执行引擎的实现细节。 你只是不应该在脑海中想象它们存储在堆栈中,以了解它是如何工作的。 这是因为捕获的变量(即使是基元变量!)不能存储在堆栈帧上,因为当函数退出时,其堆栈帧会被破坏,但捕获的局部变量(基元或非基元)必须存在于该时刻之后。 当然,为了获得最佳性能,可以在堆栈上创建未捕获的变量。 – srgstm公司 评论 2023年11月24日10:53 -
函数the_closure(){ 变量x=4; 返回函数(){ 返回x;// 这里,我们回头看看_closure中x的值 } } var myFn=关闭(); myFn();//=> 4
函数outerFunction(){ var outerVar=“猴子”; 函数innerFunction(){ 警报(outerVar); } innerFunction(); } outerFunction();
函数outerFunction(){ var outerVar=“猴子”; 函数innerFunction(){ 返回outerVar; } return innerFunction; } var referenceToInnerFunction=外部函数(); 警报(referenceToInnerFunction());
函数outerFunction(){ var outerVar=“猴子”; 函数innerFunction(){ 返回outerVar; } return innerFunction; } var referenceToInnerFunction=外部函数(); 警报(referenceToInnerFunction()); outerFunction=空; 警报(referenceToInnerFunction());
函数outerFunction(){ var outerVar=“猴子”; 函数innerFunction(){ 警报(outerVar); } outerVar=“大猩猩”; innerFunction(); } outerFunction();