当前位置: 首页 > 后端技术 > Node.js

Javascript闭包简单介绍

时间:2023-04-03 23:12:20 Node.js

一、简介闭包(closures)是Javascript语言中的一个难点。他们在面试中经常被问到,也是它的特点。许多高级应用程序都依赖于闭包。本文尽量使用通俗易懂的语言来解释闭包的概念、其形成条件以及常见的面试题。我们先看一个例子:varn=999;functionf1(){console.log(n);}f1()//999上面的代码中,函数f1可以读取全局变量n。但是,不能从函数外部读取在函数内部声明的变量。functionf1(){varn=999;}console.log(n)//UncaughtReferenceError:nisnotdefined上面代码中,在函数f1内部声明的变量n在函数外部无法读取。如果有时候需要获取函数中的局部变量。通常,这是不可能的,只能通过变通办法实现。即在函数内部,定义另一个函数。functionf1(){varn=999;functionf2(){console.log(n);//999}}在上面的代码中,函数f2在函数f1内部。此时f1里面的所有局部变量,对于f2都是可见的。既然f2可以读取f1的局部变量,那么只要把f2作为返回值,我们就可以在f1之外读取它的内部变量了!2、什么是闭包我们可以将上面的代码修改如下:functionf1(){vara=999;函数f2(){console.log(a);}返回f2;//f1返回对f2的引用}varresult=f1();//结果是f2函数result();//执行结果,全局作用域中没有a的定义,//但是函数闭包可以记录一起定义函数时的作用域Hold,输出999上面代码中,函数f1的返回值就是函数f2.由于f2可以读取f1的内部变量,所以可以从外部获取f1的内部变量。闭包是一个函数f2,一个可以读取其他函数内部变量的函数。由于在JavaScript语言中,只有函数内部的子函数才能读取内部变量,所以闭包可以简单理解为“函数内部定义的函数”。闭包最大的特点就是可以“记住”它诞生的环境。比如f2记住了它出生的环境f1,那么f1的内部变量就可以从f2中得到。本质上,闭包是连接函数内部和函数外部的桥梁。那么闭包到底是什么?当一个函数可以记住并访问它所在的词法范围时,就会发生闭包,即使该函数是在当前词法范围之外执行的。----《你不知道的Javascript上卷》我个人的理解是,闭包是函数中的函数(其他语言不能在函数中嵌套函数),函数里面可以访问函数外的变量,而函数外的变量是内部函数的一部分。闭包形成的条件函数嵌套内部函数引用外部函数的局部变量3.闭包的特点每个函数都是一个闭包,每个函数天生能够记住它被定义的作用域环境.从定义它的范围中删除一个函数,然后运行它。这个函数实际上可以记住定义时的作用域。无论函数走到哪里,定义时的作用域都带到了那里。下面我们用两个例子来说明这个问题://例子1varinner;functionouter(){vara=250;inner=function(){alert(a);//这个函数虽然是在外面执行的,但是可以记住定义的时候的作用域,a是250}}outer();vara=300;inner();//函数执行的时候会去闭包里面找变量,忽略当前作用域。//例2functionouter(x){functioninner(y){console.log(x+y);}returninner;}varinn=outer(3);//将数字3传给外层函数后,内层函数x会记住这个值inn(5);//当内层函数传入5时,它只会给y赋值,所以最后会弹出8。域,包括全局作用域和私有作用域,它们什么时候释放内存?全局作用域——只有当页面关闭时,全局作用域才会破坏私有作用域——只有函数执行才会在正常情况下产生一个新的私有作用域,当私有作用域中的代码执行完后,我们当前的作用域就会是主动释放并销毁。但是当函数执行返回一个引用数据类型的值,并且被函数外的其他东西接收时,在这种情况下,一般形成的私有作用域不会被破坏。如下面这种情况:functionfn(){varnum=100;returnfunction(){}}varf=fn();//fn执行形成的privatescope不能再销毁,即如上这段代码中,fn函数内部的private作用域会一直被占用,出现内存泄漏。所谓的内存泄漏是指在您不再拥有或不再需要它之后仍然存在的任何对象。闭包不能滥用,否则会造成内存泄漏,影响网页性能。闭包用完后,应该立即释放资源,并将引用变量指向null。接下来看一道关于内存泄漏的经典面试题:functionouter(){varnum=0;//internalvariablereturnfunctionadd(){//通过return返回add函数,在outer之外可以访问functionnum++;//内部函数有引用,作为add函数的一部分console.log(num);};}varfunc1=outer();func1();//实际调用的是add函数,输出1func1();//输出2因为outer函数内部的private作用域会一直被占用varfunc2=outer();func2();//输出1每次函数被重新引用,闭包都是全新的。func2();//输出25.闭包函数1.可以读取函数内部的变量。2、变量的值可以长期保存在内存中,生命周期比较长。因此,闭包不能被滥用,否则会导致网页出现性能问题。3、可以用来实现JS模块。JS模块:具有特定功能的js文件,将所有数据和功能封装在一个函数(私有)内部,只暴露一个包含n个方法的对象或函数。模块的使用者只需要通过模块调用方法暴露的对象就可以实现相应的功能。详情请看下面的例子://index.html文件//myModule.js文件(function(){varmsg='北京'//私有数据//操作数据的函数functiondoSomething(){console.log('doSomething()'+msg.toUpperCase())}functiondoOtherthing(){console.log('doOtherthing()'+msg.toLowerCase())}//暴露对象(两种方法供外部使用)window.myModule2={doSomething:doSomething,doOtherthing:doOtherthing}})()六、闭包的使用我们需要实现这样一个需求:点击一个按钮,提示“第n个按钮被点击”,这里我们不使用事件代理:。....没想到,点击任意按钮,后台会弹出“第四个",这是因为i是一个全局变量,当点击事件执行的时候,i的值为3。如何修改呢?最简单的方法就是用let声明ifor(leti=0;i