【3分钟带你学】 JS闭包
一、什么是闭包?
var n=99; function f1(){ console.log(n); } f1(); //99
JavaScript中有两种作用域,全局作用域和局部作用域,函数内部可以直接读取全局变量。
函数 f1 可以读取全局变量 n。但是,在函数外部无法读取函数内部声明的变量。
function f1() { var n = 99; } f1(); console.log(n); //undefined
但是, 有时我们却需要在函数外部访问函数内部的变量,正常情况下,这是办不到的,只有通过变通方法才能实现。
那就是在函数的内部, 再定义一个函数。
function f1() { var n = 99; var f2 = function() { console.log(n); } return f2; } var f = f1(); f();
上面代码中,函数f2就在函数f1内部,这时f1内部的所有局部变量,对f2都是可见的。但是反过来就不行,f2内部的局部变量,对f1就是不可见的。
这就是JavaScript语言特有的“链式作用域”结构(chain scope),子对象会一级一级地向上寻找所有父对象的变量。所以,父对象的所有变量,对子对象都是可见的,反之则不成立。既然f2可以读取f1的局部变量, 那么只要把f2作为返回值,我们不就可以在f1外部读取它的内部变量了吗!
闭包就是函数f2, 即能够读取其他函数内部变量的函数。
由于在JavaScript语言中, 只有函数内部的子函数才能读取内部变量, 因此可以把闭包简单理解成“定义在一个函数内部的函数”。
闭包最大的特点, 就是它可以“记住”诞生的环境, 比如f2记住了它诞生的环境f1, 所以从f2可以得到f1的内部变量。
在本质上, 闭包就是将函数内部和函数外部连接起来的一座桥梁;
二、垃圾回收机制(GC)及闭包
function f1( ) { var n = 99; console.log(++n); } f1( ); //100 f1( ); //100
当我们在函数内部引入一个变量或函数时,系统都会开辟一块内存空间;
还会将这块内存的引用计数器进行初始化,初始化值为0
如果外部有全局变量或程序引用了这块空间,则引用计数器会自动进行+1操作
当函数执行完毕后,变量计数器重新归零,系统会运行垃圾回收机制,将函数运行产生的数据销毁;
如计数器不是 0 ,则不会清除数据;
这个过程就称之为 "JS的垃圾回收机制" ;
但是,如果将代码该为闭包形式:
function f1() { var n = 99; function f2(){ console.log(++n); } return f2; } var f = f1(); f(); //100 f(); //101
运行代码发现,函数调用一次,其变量 n 变化一次;
因函数f1被调用时,返回的结果是f2函数体,也就是说,f2函数被当作值返回给f1的调用者,
但是f2函数并没有在此时被调用执行,
所以整个 f1 函数体,无法判断子函数f2会对其产生何种影响,无法判断 变量n是否会被使用;
即使f1函数被调用结束, 整个f1函数始终保留在内存中,不会被垃圾回收机制回收;
闭包的作用
闭包的最大用处有两个,一个是可以读取函数内部的变量,另一个就是让这些变量始终保持在内存中.此外,还保护变量的安全,因为函数内的变量只能通过函数内部访问,其他途径不能访问。
注意, 外层函数每次运行,都会生成一个新的闭包,而这个闭包又会保留外层函数的内部变量,所以内存消耗很大。
因此不能滥用闭包, 否则会造成网页的性能问题。
闭包的应用实例
tab切换
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <style type="text/css"> * { padding-bottom: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; font-size: 12px; padding-top: 0px; } BODY { padding-left: 20px; padding-top: 20px; } .wid240 { width: 242px; margin-bottom: 20px; } .wid180 { width: 182px; } .tab { border-bottom: #000 1px solid; border-left: #000 1px solid; border-top: #000 1px solid; border-right: #000 1px solid; } .tab UL { zoom: 1; clear: both; } .tab UL:after { display: block; height: 0px; visibility: hidden; clear: both; content: ""; } .tab UL LI { text-align: center; line-height: 26px; width: 60px; display: inline; background: #000; float: left; height: 26px; color: #fff; } .tab UL LI.on { background: #fff; color: #000; } .tabList { border-bottom: #000 1px solid; border-left: #000 1px solid; height: 150px; border-top: #000 1px; border-right: #000 1px solid; } .tabList .one { padding-bottom: 10px; padding-left: 10px; padding-right: 10px; display: none; color: #ff0000; padding-top: 10px; } .tabList .block { display: block; } </style> <meta name="GENERATOR" content="MSHTML 8.00.7600.16535"> </head> <body> <div class="wid240"> <div class="tab"> <ul> <li id="one1" class="on">one1 </li> <li id="one2">one2 </li> <li id="one3">one3 </li> <li id="one4">one4 </li> </ul> </div> <div class="tabList"> <div id="cont_one_1" class="one block"> cont_one_1</div> <div id="cont_one_2" class="one"> cont_one_2</div> <div id="cont_one_3" class="one"> cont_one_3</div> <div id="cont_one_4" class="one"> cont_one_4</div> </div> </div> </div> </body> <script type="text/javascript"> //获取li标签 var lis = document.getElementsByTagName('li'); //绑定悬浮事件 for(var i=0;i<lis.length;i++) { lis[i].onmouseover = (function(index){ return function() { //切换div.one var num = parseInt(index+1); var div = document.getElementById('cont_one_'+num); //判断 var divs = document.querySelectorAll('.one'); for(var i=0;i<divs.length;i++) { divs[i].style.display = 'none'; } div.style.display = 'block'; } })(i); } </script> </html>